While Ship Shape engineers are deep practitioners of JavaScript, and are often working on web applications for our clients, we are sometimes also creating services for these applications; to service functions for the app or provide the data layer and persistence in the form of an API.

We've used a number of languages for these services, but usually default to our expertise in JavaScript and work within the Node.js run-time. This works well and we then integrate into whatever deployment pipeline the client may have in place. Recently, we've started creating these services with a serverless cloud solution, rather than deploying to server infrastructure.

Serverless allows abstraction from the infrastructure. You will not have to provision, scale, or maintain servers to execute your applications, databases and/or storage systems. There is no active management, so developers are more empowered to create functions as a service and deploy directly, as well as frequently.

What we'll work on today

A common need for teams today in their applications is a GraphQL server as the API for data persistence. While not challenging to setup a basic service for use locally, there tend to often be quite a bit more steps for deployment of these APIs for use in various environments. We'll use the Serverless framework to get us started quickly and give us the ability to deploy to many common cloud providers.

npx serverless

If we follow the prompts, this will give us a basic serverless project with all we need to get started. We can change the serverless.yml to define our function and the handler specifically for HTTP requests by adding the following:

functions:
  graphql:
    handler: handler.graphql
    events:
      - http:
          path: graphql
          method: post

For our needs, we'll want to also add a package for GraphQL and I'm going to use apollo-server-lambda.

yarn add apollo-server-lambda

Now let's change our function to create a GraphQL server and use it to respond to query requests. For our demonstration, we'll just have it respond to a string for a simple query.

import { ApolloServer, gql } from 'apollo-server-lambda';

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

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

const server = new ApolloServer({
  typeDefs,
  resolvers,
  introspection: true // this is nice in testing with GraphQL playground
});

export const graphql = server.createHandler();

Try this out locally

Now that you have a basic Lambda as your GraphQL server, you probably want to try it out before getting it out on the internet. Not that this is a requirement, most free tiers of providers make this a free experiment, and so there's no risk to pushing and testing. That said, a couple of packages also make it simple to test your function locally.

For our basic needs, we'll use the serverless-offline plugin to emulate AWS Lamdba and API Gateway services on a local server. In this regard we are choosing a vendor in a way, but for our purposes, of choosing a path and pushing the code somewhere, AWS is the one I'm personally most familiar with, and it is the stated provider by default when generating a serverless project.

Also, I like to write in module syntax, so adding serverless-bundle to handle basic Webpack configuration for me gives me that flexibility. It can be used for adding Typescript, ESLint, Babel transpiling, and to generate source maps. For this use case though, we'll keep it simple with just our friend JavaScript.

At this point, I need to add the serverless package as a dev dependency to my project. This is because the initialization used npx and does not add it to the project. We didn't have a package.json file until after we added the first dependency.

yarn add serverless serverless-offline serverless-bundle -D

We then just add the config for the plugins to serverless.yml at the root:

plugins:
  - serverless-bundle
  - serverless-offline

custom:
  bundle:
    packager: yarn
  serverless-offline:
    httpPort: 3000

Next, we can test our function by running the offline start command. Note, I'm using yarn to prepend my command so that it looks first at local dependencies for the executable.

yarn serverless offline

and should give us a nice console output of the following:

Serverless: Bundling with Webpack...
Serverless: Watching for changes...
offline: Starting Offline: dev/us-east-1.
offline: Offline [http for lambda] listening on http://localhost:3002
offline: Function names exposed for local invocation by aws-sdk:
           * query: graphql-article-dev-query

   ┌─────────────────────────────────────────────────────────────────────────┐
   │                                                                         │
   │   POST | http://localhost:3000/dev/graphql                              │
   │   POST | http://localhost:3000/2015-03-31/functions/query/invocations   │
   │                                                                         │
   └─────────────────────────────────────────────────────────────────────────┘

offline: [HTTP] server ready: http://localhost:3000 🚀
offline:
offline: Enter "rp" to replay the last request

I use GraphQL Playground for my local query testing. After starting that and entering the URL given to me by serverless-offline, I'm able to add my simple "hello" query and get a response.

GraphQL playground

Put our new service somewhere

Now that we know our function works and returns the expected query value, how do we get that in the cloud for use? Assuming you have AWS setup and your CLI credentials (which we don't cover creating here, but this is a nice article about serverless and getting started on AWS), then we can run the following:

yarn serverless deploy

Serverless: Bundling with Webpack...
Serverless: No external modules needed
Serverless: Packaging service...
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
........
Serverless: Stack create finished...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service graphql-article.zip file to S3 (1.58 MB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
..............................
Serverless: Stack update finished...
Service Information
service: graphql-article
stage: dev
region: us-east-1
stack: graphql-article-dev
resources: 11
api keys:
  None
endpoints:
  POST - https://1yuhqyb55e.execute-api.us-east-1.amazonaws.com/dev/graphql
functions:
  query: graphql-article-dev-query
layers:
  None

Which results in the creation of my stack and deployment of my function with API Gateway. Armed with the endpoint generated, we can test our new GraphQL server. Using the playground again, we can see the result and I'm also showing the schema displayed from the introspection query.

GraphQL playground result from AWS

In closing

This is just one basic example of how you can use serverless computing to create services and iterate frequently without infrastructure overhead. Learning this technology has given me some of the same pleasures I recall when I was first creating for the web and could see those results in the browser immediately. I look forward to exploring this outlet in future posts and please feel free to reach out to us with any questions or requests for future discssions around this topic.