Basic Service Example

A complete HTTP API with Lambda functions — from serverless.yml to deployed infrastructure

What you'll build

A simple REST API backed by two Lambda functions: one that lists items and one that creates items. The example shows how sls.tf translates a standard Serverless Framework configuration into Terraform resources.

Project structure

my-api/
  modules/
    sls.tf/              # git submodule
  src/
    list.js
    create.js
  serverless.yml
  main.tf
  outputs.tf

serverless.yml

service: items-api
provider:
  name: aws
  runtime: nodejs20.x
  region: us-east-1
  stage: dev
  environment:
    TABLE_NAME: items-${self:provider.stage}

functions:
  listItems:
    handler: src/list.handler
    description: "List all items"
    memorySize: 256
    timeout: 10
    events:
      - http:
          path: /items
          method: get
          cors: true

  createItem:
    handler: src/create.handler
    description: "Create a new item"
    memorySize: 256
    timeout: 10
    events:
      - http:
          path: /items
          method: post
          cors: true

resources:
  Resources:
    ItemsTable:
      Type: AWS::DynamoDB::Table
      Properties:
        TableName: items-${self:provider.stage}
        BillingMode: PAY_PER_REQUEST
        AttributeDefinitions:
          - AttributeName: id
            AttributeType: S
        KeySchema:
          - AttributeName: id
            KeyType: HASH

main.tf

terraform {
  required_version = ">= 1.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "us-east-1"
}

module "serverless" {
  source      = "./modules/sls.tf"
  config_path = "${path.root}/serverless.yml"

  # Grant both functions access to the DynamoDB table
  environment_vars = {
    STAGE = "dev"
  }
}

outputs.tf

output "api_url" {
  description = "Base URL for the deployed API"
  value       = module.serverless.api_gateway_invoke_url
}

output "list_items_arn" {
  value = module.serverless.function_arns["listItems"]
}

output "create_item_arn" {
  value = module.serverless.function_arns["createItem"]
}

output "table_name" {
  value = module.serverless.custom_dynamodb_table_names["ItemsTable"]
}

Deploy

terraform init
terraform plan
terraform apply

# Output example:
# api_url = "https://abc123.execute-api.us-east-1.amazonaws.com/dev"

Test the API

# List items
curl https://abc123.execute-api.us-east-1.amazonaws.com/dev/items

# Create an item
curl -X POST https://abc123.execute-api.us-east-1.amazonaws.com/dev/items \
  -H "Content-Type: application/json" \
  -d '{"name": "my item"}'

What sls.tf created

  • Two aws_lambda_function resources (listItems, createItem)
  • Two aws_iam_role execution roles with CloudWatch Logs permissions
  • One aws_api_gateway_rest_api with routes for GET /items and POST /items
  • CORS configuration on both routes
  • One aws_dynamodb_table from the resources: block
  • Lambda permissions for API Gateway to invoke each function

Next steps