DEV Community

Tomasz Łakomy for AWS Community Builders

Posted on • Edited on • Originally published at cloudash.dev

Build a simple GraphQL server with Apollo Server and AWS CDK

(Before we start - if you've never heard of AWS Cloud Development Kit, I've created a quick, 64 minute course on egghead.io - Build an App with the AWS Cloud Development Kit)

First steps

Let's start by creating an empty AWS CDK project, to do that run:

mkdir graphql-lambda #the name of the project is up to you
cd graphql-lambda
cdk init app --language=typescript
Enter fullscreen mode Exit fullscreen mode

After installing all necessary dependencies and creating a project run npm run watch in order to enable a TypeScript compiler in a watch mode.

Next, open up the newly created project in your editor of choice (I'm using VSCode but this makes no difference here).

There's an empty CDK stack created for us lib/graphql-lambda-stack:

import * as cdk from '@aws-cdk/core';

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

        // The code that defines your stack goes here
    }
}
Enter fullscreen mode Exit fullscreen mode

Create a Hello, World! lambda function + an API Gateway

Let's start small - before we create a GraphQL server with Apollo Server, let's ship a "Hello, World!" lambda with CDK. We're going need two packages - @aws-cdk/aws-lambda and @aws-cdk/aws-apigateway.

In a separate terminal window (remember not to kill npm run watch process) run:

npm install @aws-cdk/aws-lambda @aws-cdk/aws-apigateway
Enter fullscreen mode Exit fullscreen mode

While you're waiting for npm install to finish, go ahead and create a new lambda directory in the root of your project. Inside of it, create a graphql.ts file with following content:

exports.handler = async function () {
    return {
        statusCode: 200,
        headers: { 'Content-Type': 'text/plain' },
        body: `Hello, world!`,
    };
};
Enter fullscreen mode Exit fullscreen mode

As you can see, this lambda function is not doing anything GraphQL related (yet!) - just a plain old "Hello World!".

Next, import previously installed packages in lib/graphql-lambda-stack, as well as path package from node:

import * as lambda from '@aws-cdk/aws-lambda';
import * as apiGateway from '@aws-cdk/aws-apigateway';
Enter fullscreen mode Exit fullscreen mode

And create a lambda function with lambda imported from @aws-cdk/aws-lambda:

const graphqlLambda = new lambda.Function(this, 'graphqlLambda', {
    // Where our function is located - in that case, in `lambda` directory at the root of our project
    code: lambda.Code.fromAsset(path.join(__dirname, '../lambda')),
    // What should be executed once the lambda is invoked - in that case, the `handler` function exported by `graphql.ts`
    handler: 'graphql.handler',
    // Our runtime of choice - in that case, node.js 12.x
    runtime: lambda.Runtime.NODEJS_12_X,
});
Enter fullscreen mode Exit fullscreen mode

and expose this function via an API Gateway:

new apiGateway.LambdaRestApi(this, 'graphqlEndpoint', {
    handler: graphqlLambda,
});
Enter fullscreen mode Exit fullscreen mode

At this point, your lib/graphql-lambda-stack should look more or less like this:

import * as path from 'path';
import * as cdk from '@aws-cdk/core';
import * as lambda from '@aws-cdk/aws-lambda';
import * as apiGateway from '@aws-cdk/aws-apigateway';

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

        const graphqlLambda = new lambda.Function(this, 'graphqlLambda', {
            code: lambda.Code.fromAsset(path.join(__dirname, '../lambda')),
            handler: 'graphql.handler',
            runtime: lambda.Runtime.NODEJS_12_X,
        });

        new apiGateway.LambdaRestApi(this, 'graphqlEndpoint', {
            handler: graphqlLambda,
        });
    }
}
Enter fullscreen mode Exit fullscreen mode

Awesome! Let's run cdk deploy and test if our "Hello, world!" function works fine!

This is going to take a minute but if everything goes fine - you'll get an endpoint that you can curl (or visit in a browser) to witness the glory of a "Hello, World!" message.

Adding Apollo Server

Next, go into the lambda directory and run npm init -y in order to create a package.json with all the default values.

Once this is done, run:

npm install apollo-server-lambda graphql
Enter fullscreen mode Exit fullscreen mode

inside of lambda directory. Your package.json should look more or less like this:

{
  "name": "lambda",
  "version": "1.0.0",
  "description": "",
  "main": "graphql.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "apollo-server-lambda": "^2.19.0",
    "graphql": "^15.4.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

Next, add replace the contents of graphql.ts with:

const { ApolloServer, gql } = require('apollo-server-lambda');

const typeDefs = gql`
    type Query {
        hello: String
    }
`;

const resolvers = {
    Query: {
        hello: () => 'Hello world!',
    },
};

const server = new ApolloServer({
    typeDefs,
    resolvers,
});

exports.handler = server.createHandler();
Enter fullscreen mode Exit fullscreen mode

As we can see, our lambda function will now import apollo-server-lambda (which is an AWS Lambda integration of Apollo GraphQL Server).

Next up it'll create typeDefs - currently we have a single field hello which is of type String but this can be obviously extended.

This GraphQL server has a single resolver - when the server will be asked for hello, it'll return Hello world! (but this time from a GraphQL server!).

And lastly we're creating the ApolloServer passing in the typeDefs and resolvers and using the createHandler() call to create a lambda function handler.

Before we deploy this definitely-production-ready server, let's make sure we can test that easily by enabling the in-browser GraphQL playground:

const server = new ApolloServer({
    typeDefs,
    resolvers,
    // If you'd like to have GraphQL Playground and introspection enabled in production,
    // the `playground` and `introspection` options must be set explicitly to `true`.
    playground: true,
    introspection: true,
});
Enter fullscreen mode Exit fullscreen mode

Nice! Now our GraphQL server is ready to be deployed, let's run cdk deploy once more.

After a successful deploy, copy&paste the endpoint address provided by CDK into the browser.

Let's test our server, run a following query in the GraphQL playground:

query {
  hello
}
Enter fullscreen mode Exit fullscreen mode

and if you'll receive a following response

{
  "data": {
    "hello": "Hello world!"
  }
}
Enter fullscreen mode Exit fullscreen mode

then congrats, you've just shipped an AWS Lambda powered Apollo GraphQL server with AWS Cloud Development Kit (feel free to add this to your LinkedIn!).

You can check out the source code for this example here: https://github.com/tlakomy/cdk-graphql-lambda-example

Top comments (0)