Building a Serverless REST API

May 12, 2024

4 mins

In this post, I will discuss how I have approached building a serverless REST API for a recent side project. By leveraging the power of serverless technologies, I was able to create a scalable and cost-effective API that met the requirements of my application.

Tools and Technologies Used

I used the following tools and technologies to build the serverless REST API:

  • tsoa: A TypeScript-first framework for building API services with Node.js.
  • koa: A web framework designed for building web applications and APIs in Node.js. express vs koa
  • CDK: Cloud Development Kit (CDK) is a Infrastructure as Code (IaC) tool that allows you to define your cloud infrastructure using familiar programming languages like TypeScript.
  • serverless-http: A utility that allows you to wrap a Koa or Express application in a Lambda handler function.
  • AWS Lambda: A serverless compute service that allows you to run code without provisioning or managing servers.
  • Amazon API Gateway: A fully managed service that makes it easy for developers to create, publish, maintain, monitor, and secure APIs at any scale. Easily integrates with Lambda functions.
  • AWS SDK: A JavaScript library for interacting with AWS services like S3 and DynamoDB from your web applications.

Setting Up the Project

I created a pnpm monorepo which included the cdk app and tsoa api. Having the entire project in a single repository made it easier to manage and deploy the infrastructure and API together. Creating Infrastructure and API in TypeScript allowed me to reduce the cognitive overhead of switching between languages and iterate faster.

tsoa

tsoa docs were great for getting familiar with the available decorators and I was able to quickly create a simple API with a few endpoints. I also used the decorators to add authentication and authorization. tsoa support express, koa, and hapi out of the box. I chose koa as it supports a higher traffic load and is more lightweight than express. However with the serverless architecture, the higher transaction rate of koa is not as important as the cold start time of the lambda function.

Deploying to AWS Lambda

I used esbuild to bundle the tsoa serverless endpoints and then deployed it to AWS Lambda using CDK. Configuring esbuild was a bit tricky but I was able to get it working with the following configuration:

const esbuild = require('esbuild');

esbuild.build({
  entryPoints: ['src/serverless.ts'],
  outfile: 'dist/serverless.js',
  bundle: true,
  minify: false,
  alias: {
    'tsoa': require.resolve('tsoa'),
  },
  platform: 'node',
  target: 'node20',
  external: ['@aws-sdk/client-s3', '@aws-sdk/client-dynamodb'],
}).catch(() => process.exit(1));

In the bundle aws sdk client libraries are externalized to reduce the size of the bundle as they are already available on the Lambda runtime. The bundle is unmified to make debugging easier. I kept running into issues with resolving the tsoa module as there is also a tsoa.json file in the project root. I had to alias the tsoa module to the correct path to get it working.

One of the challenges I ran into was running into timeout issues as my API grew in complexity. I had to increase the execution timeout and memory allocation for the Lambda function to handle the increased load.

Authentication and Authorization

I started by implementing Authentication using AWS Cognito due to its ease of integration with API Gateway but I pretty soon realized Cognito is a horrible choice for a managed authentication service. I briefly looked into Auth0, WorkOS, and Clerk. I didn't use Auth0 because of Okta's reputation with the recent security breaches and insane pricing. WorkOS and Clerk were both solid options. I went with WorkOS for generous free tier and good hosted UI. I used WorkOS JWT to authenticate user and then used koa-session to persist the session and renew the JWT token as needed.

Conclusion

Building a serverless REST API using tsoa, CDK, AWS Lambda, and API Gateway was a great learning experience. I was able to create and deploy a scalable and cost-effective API that met the requirements of my application. I learned a lot about serverless technologies and how to leverage them to build powerful APIs. I have created a template project that will help me quickly bootstrap new projects in the future. The template project is available on GitHub.