Published on

Build a simple GraphQL server with Apollo Server and AWS CDK

Authors
Tired of using AWS Console? 🤕
Time to boost your productivity with Cloudash — an AWS desktop client.

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

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
  }
}

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

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!`,
  };
};

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";

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,
});

and expose this function via an API Gateway:

new apiGateway.LambdaRestApi(this, "graphqlEndpoint", {
  handler: graphqlLambda,
});

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,
    });
  }
}

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

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"
  }
}

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();

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,
});

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
}

and if you'll receive a following response

{
  "data": {
    "hello": "Hello world!"
  }
}

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

I'd like to learn more, how can I do that?

Check out those resources to learn more:

Tired of switching between AWS console tabs? 😒

Cloudash provides clear access to CloudWatch logs and metrics, to help you make quicker decisions.
Try it for free:

Logs screen