https://serverless-stack.com/ logo
#sst
Title
# sst
d

Derek Kershner

03/26/2022, 4:28 PM
@thdxr, had an idea that I tried this morning: Not using any framework (besides graphql.js, “graphql” in npm) or running a server at all for a graphql lambda. Was surprisingly easy, and even Apollo specific things like
apollo-server-errors
work out of the box (because
apollo
uses graphql.js). ESM now works, and my package size dropped by ~1.8MB, which made cold starts about half as long. Happy to share more if it is interesting.
t

thdxr

03/26/2022, 4:33 PM
that's almost what we're doing in the new graphql stack
we're using helix instead of apollo, probably not as bare bones as just graphql.js but we got esm working + the bundle is a lot smaller
d

Derek Kershner

03/26/2022, 4:37 PM
I am assuming express is involved (helix typically works this way), so the added bulk might actually be kinda significant. express v4.17.3 ❘ Bundlephobia
In case bundlephobia just went down, its about 450/225KB.
For our API which has ~100 resolvers, this would amount to around a 20% increase
graphql.js is 165/39KB
t

thdxr

03/26/2022, 4:49 PM
Express isn't involved! Helix isn't a GraphQL server itself it's just utility functions, we wrote the connect to AWS lambda events
d

Derek Kershner

03/26/2022, 5:01 PM
alright, that sounds close enough, certainly. You can rest assured telling people it is a solid replacement, we have a fairly complicated use case and leaned heavily on Apollo, and it was still really, really easy. I think the only thing that would hang people is caching or middleware.
I might also phrase it as using
graphql.js
, rather than Helix as the primary framework (whenever you make the docs). When you told me this, I looked up Helix, compared to Apollo, and wanted maturity. If I had looked up
graphql.js
, and noticed
apollo
used it, I would have been MUCH more inclined to do this earlier. I also saw Express on the Helix example when I looked it up and that was also a bad taste.
t

thdxr

03/26/2022, 5:09 PM
that's a good point we should keep that in mind
Helix is used by a few full fledged gql servers (graphql-yoga for example) and in general I want to bet on the people behind it
They also make all the GraphQL codegen stuff, graphql-scalars which is also great
They have an upcoming typed frontend tool coming out soon
d

Derek Kershner

03/26/2022, 5:12 PM
I am using
graphql-compose
for these types of purposes, but my experience has been mixed. It definitely works. I don’t have experience with Helix to compare.
in any case, based on everything you have said combined with my experiences, I think you are definitely on or near the right track with regards to GraphQL + lambda.
I’m about to convert two more even larger APIs, ill post if anything goes awry, but I don’t expect it to.
t

thdxr

03/26/2022, 6:34 PM
yeah that'll be helpful, I'm happy with where the base is but I'm not sure what happens when you have a bunch of resolvers + real code in the mix
d

Derek Kershner

03/26/2022, 7:12 PM
No issues at all, aside from one interface change from
graphql
15 to 16 (resolveType for unions)
t

thdxr

03/29/2022, 1:50 AM
do you have some sample code converting a lambda event into something that invokes a GraphQLSchema in graphqljs?
d

Derek Kershner

03/29/2022, 4:01 PM
@thdxr
Copy code
const handler: APIGatewayProxyHandlerV2WithJWTAuthorizer = async (
    event,
    context
) => {
    const graphqlContext: LambdaContextFunctionParams<
        DataLoaders,
        undefined
    > = {
        event,
        context,
        express: undefined,
        loaders: dataLoaders,
    };

    if (!event.body) {
        throw new Error("No body");
    }

    const parsedBody = JSON.parse(event.body);

    graphQlLogger.log("Request started! Query:\n" + parsedBody.query, {
        label: "handler",
    });

    const response = await graphql({
        schema,
        source: parsedBody.query,
        contextValue: graphqlContext,
        variableValues: parsedBody.variables,
    });

    return {
        statusCode: 200,
        body: JSON.stringify(response),
        headers: {
            "Content-Type": "application/json",
        },
    };
};
You may see a few useless things, these are to match Apollo because: • We use libraries that work with Apollo. • Just in case this somehow fails and we need to revert (unlikely, but no downside other than these useless things).
schema
and
dataLoaders
are instantiated outside the handler (so that it only fires on cold start).
t

thdxr

03/29/2022, 4:35 PM
nice thanks will look at this soon
d

Derek Kershner

03/29/2022, 9:28 PM
Realized that the dataLoaders should be INSIDE the handler, just an FYI.