the/experts. Blog

Cover image for Apollo GraphQL API - AWS CDK & Deployment to Lambda
Mitchel Wijt
Mitchel Wijt

Posted on • Updated on

Apollo GraphQL API - AWS CDK & Deployment to Lambda

Hey there! Welcome to this new blog post!

I am currently learning a thing or 2 about AWS CDK, Lambda and Apollo GraphQL. In this post I would like to explain how to setup your AWS CDK and deploy your Apollo GraphQL API to Lambda.

Have a nice read :)

Setup AWS CDK

In order to follow this post I assume you have configured your AWS CLI with your credentials. If not, you can do that following this link

Assuming you have a fully functioning Apollo GraphQL API that runs on your local machine, it's now time to setup the AWS CDK and make sure your API gets deployed to AWS Lambda successfully.

Let's begin with the libraries we need to install. Apollo has 2 libraries. The normal one which is apollo-server and the lambda one which is apollo-server-lambda. We need to use the latter here.

In addition to this we also need 2 other libraries. Namely: aws-cdk-lib and esbuild

The reason we need to use apollo-server-lambda here and not apollo-server is because the former exposes a createHandler() method. We need this method to create a handler method for our AWS lambda function.

Once we have installed these libraries, we need to create a new directory in the root of our project. Let's call it cdk. Inside this directory let's create a stack.ts file and a cdk.json file.
Inside the stack.ts file we need to write our main logic that will be responsible for the creation of our lambda function and our API gateway.

Before we dive into that, let's import a few dependencies first:

import * as cdk from "aws-cdk-lib"
import { NodejsFunction } from "aws-cdk-lib/aws-lambda-nodejs"
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as apiGateway from "aws-cdk-lib/aws-apigateway"
import { join } from 'path'
Enter fullscreen mode Exit fullscreen mode

Next, we need to create a new class. This class will extend from the cdk.Stack class. Inside this class we can define any AWS Instance we would like to implement in our stack, but for now we will stick to the lambda function and the API gateway.

class Stack extends cdk.Stack {
    constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
        super(scope, id, props);

        const lambdaFunction = new NodejsFunction(this, "music-api-gql", {
            runtime: lambda.Runtime.NODEJS_14_X,
            handler: "graphqlHandler",
            entry: join(__dirname, '../src/handler.ts'),
            bundling: {
                minify: true
            }
        })
    }
}
Enter fullscreen mode Exit fullscreen mode

We use the NodejsFunction constructor as opposed to the normal Function constructor. The reason behind this is that the NodejsFunction constructor compiles and transpiles our TS code and utilizes the esbuild lib under the hood to bundle our code into a single file. This is exactly what we want because that makes the assets that we want to deploy to AWS Lambda significantly smaller.

If we do not bundle our code, we most likely need to zip our JS build files ourselves and add our node_modules to it. I tried this myself, but I ran into a limitation. AWS Lambda has a limit on the unzipped package size (our code) which is maxed at 250MB. Including all node_modules quickly brings the package size > 250MB which is the reason my deployment kept failing.

Here we use the NodejsFunction constructor which takes care of the bundling so we do not have to worry about this :)

Looking back at our code example, we can see that we have a couple of properties inside our constructor.

  • runtime -> Specifies our runtime environment
  • handler -> Specifies our handler function (the one we export with createHandler()). Since our code is bundles into 1 file, we do not traverse through other directories here, but look in the root for our handler function.
  • entry -> Specifies the entry point of our unbundled TS files.
  • bundling -> Specifies custom bundling logic for esbuild.

Next we create a new API gateway for our lambda function. This happens below the definition of our lambda function.


        new apiGateway.LambdaRestApi(this, "music-api-gatway", {
            handler: lambdaFunction
        })
Enter fullscreen mode Exit fullscreen mode

Now that we have defined the stack we want to use, we need to use the class to initiate it all:

const app = new cdk.App()

new Stack(app, "my-cdk-stack", {
    stackName: 'my-cdk-stack'
})
Enter fullscreen mode Exit fullscreen mode

Now our stack.ts file is complete (for now). Let's have a look at our cdk.json. The aws-cdk commands we will be using make use of this file for it's configuration. For now defining the path of our stack class will suffice.

{
  "app": "npx ts-node --prefer-ts-exts ./stack.ts"
}
Enter fullscreen mode Exit fullscreen mode

Deployment to AWS Lambda using the CDK

If you have followed the steps above and are at this stage. It means you can start deploying the API to AWS Lambda!

First we need to issue the following command:
npx aws-cdk synth → This produces (synthesizes) an AWS CloudFormation template for each stack defined in the stack.ts class. This also bundles your code and places it in the cdk.out folder which the deployment command will use to actually deploy the stack.

When this is done successfully we can issue the deploy command:
npx aws-cdk deploy

You should now see a URI in your terminal. This is your lambda API gateway URI. If all went well, that URI exposes your Apollo GraphQL API! :)

Discussion (0)