Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consider documenting alternate approaches? #38

Closed
glasser opened this issue Oct 18, 2022 · 10 comments
Closed

Consider documenting alternate approaches? #38

glasser opened this issue Oct 18, 2022 · 10 comments

Comments

@glasser
Copy link

glasser commented Oct 18, 2022

The approach taken in this package (where AWS API Gateway v1 and v2 protocols are directly parsed and converted into the appropriate format for invoking the Apollo Server 4 API) works well for many users and minimizes the impact on bundle size by directly implementing the conversion.

However, it doesn't allow the user to configure anything about the HTTP behavior of their server, and it only currently supports API Gateway. (This is similar to the tradeoffs in Apollo Server 2's apollo-server-lambda package.)

In Apollo Server 3, we rewrote apollo-server-lambda to use the @vendia/serverless-express package. This package supports a larger number of AWS products as event sources as well as even Azure Functions. It converts events from any of these sources into an Express request and invokes an Express app of your choosing. This allows you to use any Express API or middleware to customize the HTTP behavior of the server.

The new APIs in Apollo Server 4 around serverless startup actually make it straightforward to use this style of Lambda integration yourself. The following is a fully functioning Lambda AS4 server that works in a variety of AWS features:

const { ApolloServer } = require('@apollo/server');
const { expressMiddleware } = require('@apollo/server/express4');
const serverlessExpress = require('@vendia/serverless-express');
const express = require('express');
const { json } = require('body-parser');
const cors = require('cors');

const server = new ApolloServer({
    typeDefs: 'type Query { x: ID }',
    resolvers: {Query: {x: () => 'hi!'}},
});

server.startInBackgroundHandlingStartupErrorsByLoggingAndFailingAllRequests();

const app = express();
app.use(cors(), json(), expressMiddleware(server));

exports.handler = serverlessExpress({app});

You can tune HTTP behavior via standard Express APIs and middleware on app.

The downside is that this does increase the bundle size (and makes the app a bit more complex), and so it certainly may be better for folks without these extra needs to use a package like this one instead of building a handler from pre-existing pieces.

That said, it might make sense to document this sort of approach somewhere... in this package? In the Apollo Server docs? Elsewhere?

By the way, here's an example of how to write a context function which has access to both Express-style and Lambda-style information about the request:

const { ApolloServer } = require('@apollo/server');
const { expressMiddleware } = require('@apollo/server/express4');
const serverlessExpress = require('@vendia/serverless-express');
const express = require('express');
const { json } = require('body-parser');
const cors = require('cors');

const server = new ApolloServer({
    typeDefs: 'type Query { x: ID }',
    resolvers: {Query: {x: () => 'hi!'}},
});

server.startInBackgroundHandlingStartupErrorsByLoggingAndFailingAllRequests();

const app = express();
app.use(cors(), json(), expressMiddleware(server, {
    context: async ({ req, res }) => {
        const { event, context } = serverlessExpress.getCurrentInvoke();
        return {
            expressRequest: req,
            expressResponse: res,
            lambdaEvent: event,
            lambdaContext: context,
        }
    }
}));

exports.handler = serverlessExpress({ app });
@laverdet
Copy link

https://github.com/dougmoscrop/serverless-http is also worth trying, and in my experience has better support for multi-value headers (cookies) and is just overall a leaner package.

@glasser
Copy link
Author

glasser commented Nov 1, 2022

@laverdet neat, that gives a similar experience to my snippet I guess?

@laverdet
Copy link

laverdet commented Nov 1, 2022

Yeah, it's very similar but I would recommend serverless-http over vendia. I opened up tickets about multiValueHeaders headers in AWS being an "unmitigated disaster" on both projects: CodeGenieApp/serverless-express#554 dougmoscrop/serverless-http#244. AWS provides 3 distinct event and response payload types each with unique sets of capabilities and drawbacks on this 32 year old protocol (you would think AWS would have this figured out by now). None of the event versions gets HTTP "correct" but with some hacks you can make it ok.

serverless-http has special handling for cookies here: https://github.com/dougmoscrop/serverless-http/blob/cf7a0048f10f1f8cb4a68e0f9ceb3f3cca6ef6fc/lib/provider/aws/format-response.js#L13-L15

vendia's package lacks the same handling, and therefore if you try to set more than one cookie in a response what actually happens is you get a single cookie with value of something like "cookie1value; cookie2=cookie2value". If you don't send cookies in your distribution this won't be an issue obviously.

@H4ad
Copy link

H4ad commented Nov 16, 2022

Hey, I'm the maintainer of Serverless Adapter, inspired by #6032 and this issue, maybe we can try to add support for Apollo Server directly inside from my library and we could even skip creating theRequest and Response and have Apollo Server handle the requests directly.

From what I've read in the source code, I can use executeHTTPGraphQLRequest to handle the requests directly , in my library we have the Framework concept that fits in Apollo but my library is very extensible so maybe we can achieve the integration without needing to create Request and Response, just calling executeHTTPGraphQLRequest directly as inside a Custom Handler.

I'll try to add support when I have more time, this week I need to present my final theses, but if all goes well we can add support for Apollo Server on AWS, Azure, Firebase, Huawei and Digital Ocean at the same time time with faster integration than previous libraries (maybe).

@glasser
Copy link
Author

glasser commented Nov 16, 2022

@H4ad That sounds pretty interesting! IMO the hard part of doing (say) Lambda support is not "building the Apollo representation" but "parsing one of many possible event formats", so if your library already does that well, then that's helpful.

@H4ad
Copy link

H4ad commented Nov 17, 2022

@glasser That's what the library does with Adapters (AWS example), you can add as many adapters as you want, and because I use contracts between each layer in my library, I can easily integrate with any serverless cloud and any event source.

I created a proof of concept H4ad/serverless-adapter#56 (comment), if you want to give it a try I'll keep the work going and ping you once I'm done.

Just not to deviate from the topic, I think it's worth documenting the alternative approaches, in theory I can already add support for Apollo Server in my library and other libraries just targeting express. So perhaps you could add a section on serverless to the Apollo Docs and discuss about the libraries and implementations like fastify does in the serverless section instead leaving it just under API Reference.

@glasser
Copy link
Author

glasser commented Nov 17, 2022

Another option is just to link your library directly as its own thing from https://www.apollographql.com/docs/apollo-server/integrations/integration-index/ especially if it has clear examples for using AS with all the different serverless environments.

Maybe we would restructure that page to have one section/table for "web frameworks", another section for "serverless frameworks", and the second section could offer both the minimalist approach like this package (probably smaller bundle size but less configurable) and your package.

@H4ad
Copy link

H4ad commented Nov 20, 2022

The option of web frameworks and serverless frameworks sounds great to me, let me know if I can help write something.

Also, #39 could be solved by my library, I created CorsFramework, a cors wrapper that gives the power to define and customize CORS headers. I also need to create BodyParserFramework to handle JSON and other input types because my library sends buffer contents directly to the framework and Apollo expects JSON. After finish BodyParserFramework, I can finish the ApolloServerFramework.

Do you think it's worth putting ApolloServerFramework inside this repository or should I keep it inside serverless-adapter?

@H4ad
Copy link

H4ad commented Dec 3, 2022

@glasser I finished the integration with Apollo Server, you can see the documentation here.

Also, I added some example projects in this repository, the stable environments are AWS, Azure and Digital Ocean. I still need to improve Firebase support and maybe in the future I can add GCP.

About support for event sources, Api Gateway v1/v2 works great but we need special handling when we deal with SQS, SNS because it needs to be sent as mutation instead of just regular HTTP Post request. So I think if you want to use SQS with vendia you will have problem to receive HTTP Post without sent as a mutation.

Let me know if I can help with anything about the "serverless frameworks" documentation.

@BlenderDude
Copy link
Member

The new handler and middleware syntax with v2.0.0 now allows for nearly infinite customization of the parsing process, so end-users can parse events and generate responses in whatever form is necessary.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants