← All projects

Writeup

Aug 2026

Serverless Dice Roll API

A dice rolling API on Lambda and API Gateway, deployed from a SAM template.

  • AWS Lambda
  • API Gateway
  • AWS SAM CLI
  • CloudFormation
  • Node.js
  • IaC
  • curl

What I built

A small API that rolls dice and returns the result as JSON. There is no server behind it. A Lambda function does the work, API Gateway gives it a public URL, and a SAM template defines the infrastructure as code to deploy and delete both automatically.

A GET to /roll returns a six-sided roll by default, and an optional sides parameter rolls anything from 2 to 100. The function and the template are both in the repo.

The Lambda function

The handler takes the event API Gateway passes it, reads the sides value out of the query string, generates the roll, and returns a status code, headers, and a JSON body. That return shape is what API Gateway converts into the HTTP response.

index.mjs
export const handler = async (event) => {
  // API Gateway passes query string params (?sides=20) through the event object.
  const params = event.queryStringParameters || {};
  // Default to a standard 6-sided die if we don't get a value for sides.
  const sides = parseInt(params.sides) || 6;
  // A 1-sided die isn't a die, and anything more than 100 sides is just chaos.
  // We'll return a client error.
  if (sides < 2 || sides > 100) {
    return {
      statusCode: 400,
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        error: "Sides must be between 2 and 100.",
      }),
    };
  }

  // Calculate a random integer between 1 and sides.
  const result = Math.floor(Math.random() * sides) + 1;

  // Lambda functions behind API Gateway return this shape:
  // statusCode, headers, and a stringified body
  return {
    statusCode: 200,
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      sides: sides,
      roll: result,
    }),
  };
};

API Gateway

A Lambda function has no public address on its own. API Gateway provides one. It terminates TLS, maps the route to the function, passes the request in as an event, and turns the return value back into an HTTP response.

Defining it as code

All of this way deployed using infrastructure as code (IaC). The Lambda function, API Gateway, its IAM permissions, and the API output are all declared in a template file.

template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Dice Roll API - A serverless API that rolls dice

Resources:
  DiceRollFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.handler
      Runtime: nodejs24.x
      CodeUri: lambda/
      Timeout: 5
      MemorySize: 128
      Events:
        RollDice:
          Type: Api
          Properties:
            Path: /roll
            Method: get

Outputs:
  DiceRollApi:
    Description: API Gateway endpoint URL
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/roll"

Testing with curl

curl against the endpoint the stack prints as an output confirms the whole path works. A GET returns the JSON roll, and running it a few times returns different numbers, which shows the function is executing per request rather than serving something cached.

bash
$ curl "https://abcxyz.execute-api.us-east-1.amazonaws.com/Prod/roll"
{"sides":6,"roll":4}

Rolling other dice

The endpoint takes an optional sides parameter, so /roll?sides=12 returns a number between 1 and 12. It is the one piece of input the API accepts.

Anything outside 2 to 100 comes back as a 400 with a message rather than a roll.

bash
$ curl "https://abcxyz.execute-api.us-east-1.amazonaws.com/Prod/roll?sides=12"
{"sides":12,"roll":9}

$ curl "https://abcxyz.execute-api.us-east-1.amazonaws.com/Prod/roll?sides=1"
{"error":"Sides must be between 2 and 100."}