“”
Setting a GraphQL endpoint using AWS Lambda functions.

Lambda & DynamoDB

  View more techs   

Setting a GraphQL endpoint using AWS Lambda functions

 

Guillermo Osores

Guillermo Osores

February 11, 2020

 

In this article we will see how easy it is to set a GraphQL endpoint using AWS Lambda functions. We are going to assume you already know about GraphQL (here is a link so that you can learn it if you haven’t yet https://graphql.org/). First we need to Install the serverless framework from NPM. This will help to not only upload our solution to AWS but also to test it locally.


npm i -g serverless

 

You need an account to use serverless, so if you are not logged in, run this script to login or create a new account:


serverless login

 

Serverless needs to be connected to your AWS console, so running this script will guide you through the process of setting it up:


serverless

 

In the example we are going to mock our data and we are only going to use these two NPM libs: apollo-server-lambda and graphql. The first one is responsible for the GraphQL integration on AWS Lambda.


npm install apollo-server-lambda graphql

 

Create serverless.yml file to configure your serveless instance:


service: apollo-lambda provider: name: aws runtime: nodejs12.x functions: graphql: handler: src/server.handler events: - http: path: graphql method: post cors: true - http: path: graphql method: get cors: true

 

Handler property is the route to the server file plus your exported handler. This is our server file /src/server.js:


const { ApolloServer, gql } = require('apollo-server-lambda');
 
// types definitions
const typeDefs = gql`
 ${require('./types').types}
`;
 
// Provide resolver functions for your schema fields
const resolvers = {
 Query: {
   ...require('./queries').queries
 },
 Mutation: {
   ...require('./mutations').mutations
 }
};
 
// creating the server using definitions and resolvers
const server = new ApolloServer({
 typeDefs,
 resolvers,
 // context: ({ event, context }) => ({
 //   headers: event.headers,

//   functionName: context.functionName,
 //   event,
 //   context,
 // }),
});
 
// creating and exposing the handler to use in aws lambda
exports.handler = server.createHandler({
 cors: {
   origin: '*',
   // credentials: true,
 },
});

 

We set the server so that if we want to add a query or mutation, we only need to create a resolver and add it to the schema. So inside src/types we have two types of files, one exposing the type itself and the other that gathers all types and squashes them into one exported object: /src/types/index.js (the squasher)


const fs = require('fs');
 
const types = fs.readdirSync('./src/types')
 .reduce((p, f) => {
     if (f === 'index.js') return p;
     p += require(`./${f}`).default;
     return p;
   },
   ''
 );
 
exports.types = types;

 

User type in src/types/user.js


exports.default = `type User {
 first_name: String,
 last_name: String,
 email: String,
}`;

 

User type in src/types/query.js


exports.default = `type Query {
 ping: String,
 getUserInfo (id: Int): User
}`;

 

We did the same with our resolvers: /src/queries/index.js (the squasher)


const fs = require('fs');
const queries = fs.readdirSync('./src/queries')
 .reduce((p, f) => {
     if (f === 'index.js') return p;
     p[f.replace('.js', '')] = require(`./${f}`).default;
     return p;
   },
   {}
 );
 
exports.queries = queries;

 

Ping resolver /src/queries/ping.js:


const ping = () => 'pong';
exports.default = ping;

 

getUserInfo resolver /src/queries/getUserInfo.js:


const mockedData = [
 {
   id: 1,
   first_name: 'user',
   last_name: '1',
   email: 'user1@gmail.com',
 },
 {
   id: 2,
   first_name: 'user',
   last_name: '2',
   email: 'user1@gmail.com',
 }
]
 
const getUserInfo = (parent, args, context) => mockedData.filter(
 d => d.id === args.id
)[0];
 
exports.default = getUserInfo;

 

As you can see, we mocked two users, but the getUserInfo function should have whatever you need to resolve the query, like querying the database, consuming an API, etc. Serverless lib has an option to test your functions locally so you don't have to deploy to AWS to test the changes. So to test getUserInfo we can create a JSON file containing the requested information. localTests/getUserInfo.json


{
 "httpMethod": "POST",
 "headers": {},
 "body": "{\"operationName\":null,\"variables\":{\"id\":2},\"query\":\"query ($id: Int) {\\n  getUserInfo(id: $id) {\\n    first_name\\n    last_name\\n    email\\n  }\\n}\\n\"}"
}

 

Then we can use it like this:


serverless invoke local -f graphql -p localTests/getUserInfo.json

 

-f is the function defined on our serverless.yml and -p is the file mentioned above. To upload this to AWS just type:


serverless deploy

 

If all goes correctly, it will tell you the endpoint that was created. Now we can paste it on our preferred browser to get this interface to play around.

GraphQL serverless

 

Guillermo Osores

Guillermo Osores

Guillermo is a senior full stack developer with more than six years experience in software development. Specialized in React, React Native and Node, he has worked on a wide range of projects for our US clients. Guillermo is passionate about teamwork, adding value to the projects he is entrusted with, and seeing products come to life and evolve.

Contact us

Ready to get started? Use the form below or give us a call to meet our team and discuss your project and business goals.
We can’t wait to meet you!


Follow Us
See our client reviews