An efficient way to build your serverless microservices. Part 1.  Local Development.

An efficient way to build your serverless microservices. Part 1. Local Development.

Improving NodeJS Dev UX with AWS SAM, Jest and esbuild. As an example, I took a GraphQL server for serverless API.

Level:

Advanced

Source code:

github.com/javatask/graphql-serverless

The Goal of this series of articles

I had worked with AWS lambdas from days when there was no AWS SAM, and it was the early days of Serverless Frameowork. It was an interesting time to find the best tools and approaches to building serverless applications. I started with bash, webpack, npm .npmrc configs variables and console.log :) But after all these years, the industry found better, not yet perfect :), ways to develop serverless apps. In this series, I'll share the NodeJS project boilerplate that shows state-of-the-art tooling that can be used to develop, test, deploy, test, log, trace, monitor, upgrade any serverless business logic.

Series

Introduction

Building the lifecycle for NodeJS Backend for Frontend (BFF) with good Dev user experiences (UX) is challenging. But to add an additional requirement to use serverless stack and hopefully, it is still possible to prevail.

Note. This article is not targeting AWS AppSync. This article uses "raw" AWS API Gateway and AWS Lambda to build GraphQL BFF. AWS AppSync is not used in our use cases because there is a need to have an on-prem version of BFF.

Prerequirements

Knowledge of

Quick start

For those who want to go directly to code, please follow https://github.com/javatask/graphql-serverless
To run the function, you need to install dependencies on your machine:

  • [NodeJS] (nodejs.org) - recommended way to install is nvm

  • [Docker] (docker.com)

  • [AWS CLI] (aws.amazon.com/cli)

  • [AWS SAM] (aws.amazon.com/sam)
    And then run commands in your terminal:

    git clone https://github.com/javatask/graphql-serverless.git 
    cd graphql-serverless 
    npm i
    npm test 
    npm run local-run
    

Part 2. Development in the Cloud This article guides developers through the deployment and live coding capabilities of AWS SAM blog.javatask.dev/an-efficient-way-to-build..

Note. At the end of article there are changes to boilerplate for support of source maps

Note. These commands should work on Linux or MacOS

Criteria for good serverless Dev UX

There is need to account for list of features like:

After defining the criteria, there is need to check offered implementation offered in github.com/javatask/graphql-serverless.

Local development of Lambdas

Tooling and package.json

{
...
  "type": "module",
...
  "dependencies": {
    "@apollo/server": "^4.3.0",
    "@as-integrations/aws-lambda": "^1.1.0",
    "graphql-tag": "^2.12.6"
  },
  "devDependencies": {
    "esbuild": "0.16.12",
    "jest": "^29.2.1"
  },
  "scripts": {
    "test": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js",
    "local-run": "npm run build && sam local invoke --event ./events/graphql-post.json --env-vars env.json GraphqlServerFunction",
    "local-api": "npm run build && sam local start-api --env-vars env.json",
    "build": "sam build",
    "deploy": "sam deploy --guided",
    "build-deploy": "npm run build && npm run deploy",
    "cloud-watch": "sam sync --stack-name graphql-serverless --watch",
    "cloud-logs": "sam logs -n GraphqlServerFunction --stack-name graphql-serverless"
  }
...
}

ESM

To enable ESM by default, there is directive "type":"module" in package.json, it will instruct node to treat all js files as ESM.

Dependencies section

  • @apollo/server: This is the Apollo Server package, which is a popular GraphQL server for Node.js.

  • @as-integrations/aws-lambda: This is the AWS Lambda integration for Apollo Server. It allows you to deploy an Apollo Server instance as an AWS Lambda function.

  • graphql-tag: This is a utility package for parsing GraphQL queries.

And Dev dependencies

  • esbuild: This is a JavaScript and TypeScript bundler and minifier. It can be used to build and optimize the project for production.

  • jest: This is a popular test runner for JavaScript projects. It can be used to run and automate tests.

Scripts section

Scripts sections define next scripts:

  • test: This script runs the test suite for the project. It uses the jest test runner to execute the tests. There are two tests: 1)First test for business logic; 2) Second test for GraphQL query

  • local-run: This script builds the project and invokes the AWS Lambda function locally using the sam local invoke command. It passes the event data from the ./events/graphql-post.json file and the environment variables from the env.json file.

  • local-api: This script builds the project and then starts the local API Gateway using the sam local start-api command. It uses the environment variables from the env.json file. After running this command, you can query the local instance of the server with the next command

    curl --request POST 
    --url http://127.0.0.1:3000/graphql 
    --header 'Content-Type: application/json' 
    --data '{"query":"query Test {state}"}'
    
  • build: This script builds the project using the sam build command.

  • deploy: This script deploys the project to AWS using the sam deploy command. To deploy the stack, you need to pass your samconfig.toml file or use --guided key with this command

  • build-deploy: This script runs the build and deploys scripts consecutively.

  • cloud-watch: This script syncs the local file changes with the stack running in the cloud and restarts the function if needed using the sam sync command.

  • cloud-logs: This script fetches the logs for the specified AWS Lambda function using the sam logs command.

Tips and tricks for local development

When writing this article, there was no easy way to watch code changes while running the npm run local-api command. To update code for your local running server, you need to run the npm run build command and do NOT restart npm run local-api command. I recommend running two terminal windows, one running npm run local-api server and the second running npm run build command after code changes.

But the better approach is to use jest --watch to develop your business logic. You are then using jest to test your GraphQL revolvers and queries. Finally, after good code coverage is reached, run your local server to do your GraphQL service integration tests.

Note. Local server also can be used to implement integration tests with other parts of your system

Note. What if I need to use AWS service X.Y.Z. for business logic? In this case I can recommend using Hexagonal architecture to mock this services with Jest mocking user modules

Congratulations!!! Your lambda works locally!!!

Summary

AWS Serverless Application Model (SAM) gives many tools to improve developer user experiences while developing serverless microservices. Among these features are:

  • support of latest LTS NodeJS

  • integration with state-of-the-art JavaScript bundler - esbuild

  • support for local invocation of Lambdas

  • support for local invocation of API Gateway

This article references the code repository github.com/javatask/graphql-serverless and illustrates all these features. Including Jest configuration.

Part 2. Development in the Cloud

This article guides developers through the deployment and live coding capabilities of AWS SAM blog.javatask.dev/an-efficient-way-to-build..