Skip to main content

How to deploy a serverless REST API on AWS Lambda

Deploy a Node.js REST API on AWS Lambda behind API Gateway using Terraform. Covers handler code, packaging, IaC, and verification through CloudWatch.

Difficulty
Intermediate
Duration
45 minutes
Steps
6

Serverless functions let you run code without managing servers. AWS Lambda runs your handler in response to events and bills per millisecond of execution. Paired with Amazon API Gateway, it becomes a fully managed REST API that scales from zero to thousands of requests automatically.

This tutorial deploys a small Node.js API on Lambda fronted by API Gateway, provisioned with Terraform so the whole stack is reproducible.

Prerequisites

  • An AWS account and credentials configured (aws configure).
  • Node.js 20 or later.
  • Terraform 1.6 or later.

Steps

1. Write the handler

Create index.mjs:

export const handler = async (event) => {
  const name = event.queryStringParameters?.name ?? "world";
  return {
    statusCode: 200,
    headers: { "content-type": "application/json" },
    body: JSON.stringify({ message: `hello ${name}` })
  };
};

Lambda passes the HTTP request as event and expects a response object with statusCode, headers, and body.

2. Package dependencies

For a zero-dependency function, zip the source:

zip function.zip index.mjs

If you add npm packages, run npm ci --omit=dev and include node_modules in the archive.

3. Define the Lambda in Terraform

resource "aws_lambda_function" "api" {
  function_name = "hello-api"
  runtime       = "nodejs20.x"
  handler       = "index.handler"
  filename      = "function.zip"
  source_code_hash = filebase64sha256("function.zip")
  role          = aws_iam_role.lambda.arn
}

The source_code_hash ensures Terraform redeploys when the code changes.

4. Add API Gateway

Use an HTTP API, which is cheaper and simpler than the REST API type:

resource "aws_apigatewayv2_api" "http" {
  name          = "hello-http"
  protocol_type = "HTTP"
}

resource "aws_apigatewayv2_integration" "lambda" {
  api_id                 = aws_apigatewayv2_api.http.id
  integration_type       = "AWS_PROXY"
  integration_uri        = aws_lambda_function.api.invoke_arn
  payload_format_version = "2.0"
}

Add a route and a stage, and grant API Gateway permission to invoke the function with an aws_lambda_permission resource.

5. Apply and deploy

terraform init
terraform apply

Terraform prints the invoke URL as an output. Capture it for the next step.

6. Test the endpoint

curl "$(terraform output -raw api_url)?name=ada"

You should see {"message":"hello ada"}.

Verification

Check CloudWatch Logs for the function's log group to confirm invocations. A cold start shows an INIT_START line; warm invocations skip it. Send a few requests and confirm latency drops after the first.

Next Steps

Add a custom domain with API Gateway and ACM, attach least-privilege IAM, and set reserved concurrency to cap cost. Move secrets into AWS Secrets Manager rather than environment variables.

Prerequisites

  • AWS account with IAM access
  • Node.js installed
  • Terraform CLI installed

Steps

  • 1
    Write the handler
  • 2
    Package dependencies
  • 3
    Define the Lambda in Terraform
  • 4
    Add API Gateway
  • 5
    Apply and deploy
  • 6
    Test the endpoint

Category

Cloud