Skip to content

REST API built with Go & Gin Framework for AWS Lambda. Hybrid execution: Lambda functions in production, HTTP server for local development.

License

Notifications You must be signed in to change notification settings

ronihdzz/aws-lambda-go

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

7 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

AWS Lambda Go API

🌐 Languages: English | Español

A REST API built with Go and Gin Framework, designed to run both as an AWS Lambda function (using Docker containers) and as a traditional HTTP service for local development.

⚑ What does running AWS Lambda as an API mean?

AWS Lambda is a serverless computing service that executes code in response to events, without the need to provision or manage servers. When we talk about "running Lambda as API", we refer to using Lambda functions as the backend of a REST API, where each HTTP request becomes a Lambda function invocation.

πŸ”„ How does it work?

  1. Client β†’ sends HTTP request to a URL
  2. API Gateway β†’ receives the request and transforms it into a Lambda event
  3. Lambda Function β†’ processes the event and returns a response
  4. API Gateway β†’ converts the Lambda response to HTTP format
  5. Client β†’ receives the standard HTTP response
graph LR
    A[Client] --> B[API Gateway]
    B --> C[Lambda Function]
    C --> D[Process Request]
    D --> C
    C --> B
    B --> A
Loading

πŸš€ Advantages of using AWS Lambda for APIs

πŸ’° Cost Optimized

  • Pay per use: Only pay for actual execution time (billed by milliseconds)
  • No fixed costs: No servers running 24/7 consuming resources
  • Scale to zero: When there's no traffic, the cost is $0
  • Comparison: An API with 1M requests/month can cost ~$20 vs ~$50-200 on EC2

πŸ“ˆ Automatic Scalability

  • Auto-scaling: Handles from 1 to thousands of concurrent requests automatically
  • No configuration: No need to configure load balancers or auto-scaling groups
  • Instant response: Adapts to traffic spikes without manual intervention
  • Concurrency: Up to 1,000 concurrent executions by default (scalable)

πŸ“Š Ideal Use Cases

βœ… REST/GraphQL APIs with variable traffic
βœ… Microservices and event-driven architectures
βœ… Applications with unpredictable usage patterns
βœ… Startups and MVPs with limited budget
βœ… APIs requiring high availability without management

⚠️ Considerations

  • Cold starts: First invocation can take 100-1000ms (mitigable with SnapStart)
  • Time limit: Maximum 15 minutes execution per invocation
  • Memory limit: Maximum 10GB RAM per function
  • Payload limit: 6MB for synchronous requests

πŸ“‹ General Description

This application is a REST API that provides basic endpoints such as health check, echo, and server time. It's optimized for deployment on AWS Lambda using Docker containers and features an automated CI/CD pipeline with GitHub Actions.

The application uses a hybrid pattern that allows:

  • Production: Execution as AWS Lambda function through API Gateway
  • Development: Traditional HTTP server for local testing

πŸ—οΈ Project Architecture

aws-lambda-go/
β”œβ”€β”€ .github/workflows/     # CI/CD Pipeline with GitHub Actions
β”‚   └── main.yml          # Build and deploy workflow
β”œβ”€β”€ cmd/                  # Application entry point
β”‚   └── main.go          # Hybrid startup logic (Lambda/HTTP)
β”œβ”€β”€ docker_images/        # Docker configurations
β”‚   β”œβ”€β”€ deploy/          # Dockerfile for AWS Lambda
β”‚   β”‚   └── Dockerfile.deploy
β”‚   └── local/           # Dockerfile for local development
β”‚       └── Dockerfile.local
β”œβ”€β”€ internal/            # Internal application code
β”‚   β”œβ”€β”€ middleware/      # Custom middlewares
β”‚   β”‚   └── logger.go    # Logging and observability middleware
β”‚   └── router/          # Route configuration
β”‚       └── router.go    # Endpoint and handler definitions
β”œβ”€β”€ compose.yml          # Docker Compose for development
β”œβ”€β”€ go.mod              # Project dependencies
└── go.sum             # Dependencies lock file

πŸš€ Execution Modes

1. Lambda Mode (Production)

The application automatically detects if it's running in AWS Lambda using the AWS_LAMBDA_FUNCTION_NAME environment variable:

if _, inLambda := os.LookupEnv("AWS_LAMBDA_FUNCTION_NAME"); inLambda {
    lambda.Start(ginadapter.NewV2(r).ProxyWithContext)
    return
}

Features:

  • Uses aws-lambda-go-api-proxy to adapt Gin to Lambda
  • Deployed as Docker image in ECR
  • Invocation through API Gateway
  • Auto-scaling and pay-per-use billing

2. Local HTTP Mode (Development)

When Lambda environment is not detected, the application starts a traditional HTTP server:

port := os.Getenv("PORT")
if port == "" {
    port = "8080"
}
r.Run(":" + port)

Features:

  • Direct HTTP server on configurable port (default: 8080)
  • Ideal for development and local testing
  • Detailed logs with custom middleware

🐳 Docker and Containers

Dockerfile.deploy (Production/Lambda)

# Multi-stage build for AWS Lambda
FROM amazonlinux:2 as builder
# ... static compilation
FROM public.ecr.aws/lambda/go:1
COPY --from=builder /app/app ${LAMBDA_TASK_ROOT}
CMD ["app"]

Optimizations:

  • Static build (CGO_ENABLED=0)
  • Official AWS Lambda base image for Go
  • Reduced size with flags -ldflags="-s -w"

Dockerfile.local (Development)

# Multi-stage build for local development
FROM golang:1.24-alpine AS builder
# ... compilation
FROM alpine:latest
# ... minimal final image
EXPOSE 8080
CMD ["./server"]

Features:

  • Minimal Alpine image for development
  • Port 8080 exposed by default
  • CA certificates included for HTTPS

πŸ™ Docker Compose

The compose.yml file defines two services for different use cases:

app-lambda

services:
  app-lambda:
    build:
      dockerfile: docker_images/deploy/Dockerfile.deploy
    ports:
      - "9000:8080"

Usage:

# Start local Lambda emulation
docker-compose up app-lambda

# Invoke function via Lambda Runtime API
curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" \
  -d '{"httpMethod":"GET", "path":"/health"}'

app-api

services:
  app-api:
    build:
      dockerfile: docker_images/local/Dockerfile.local
    ports:
      - "9000:8080"

Usage:

# Start traditional HTTP API
docker-compose up app-api

# Consume directly via HTTP
curl http://localhost:9000/health

πŸ›£οΈ Available Endpoints

The API exposes the following endpoints defined in internal/router/router.go:

Endpoint Method Description
/ GET Welcome page with API information
/health GET Health check - returns {"status": "ok"}
/echo POST Echo service - returns the sent JSON
/time GET Current server time in RFC3339 format

Usage Examples

# Health check
curl https://your-api-gateway-url/health

# Echo service
curl -X POST https://your-api-gateway-url/echo \
  -H "Content-Type: application/json" \
  -d '{"message": "Hello World"}'

# Server time
curl https://your-api-gateway-url/time

πŸ”§ Middleware and Observability

Logging Middleware

The project includes a custom middleware in internal/middleware/logger.go:

func RequestLogger() gin.HandlerFunc {
    return func(c *gin.Context) {
        start := time.Now()
        c.Next()
        log.Printf("[REQ] %3d | %-7s | %s | %v",
            c.Writer.Status(),
            c.Request.Method,
            c.Request.URL.Path,
            time.Since(start),
        )
    }
}

Features:

  • Logging of all requests with duration
  • Consistent format: [REQ] STATUS | METHOD | PATH | DURATION
  • Automatic integration with CloudWatch Logs in Lambda

πŸš€ CI/CD Pipeline

The GitHub Actions workflow (.github/workflows/main.yml) automates the complete deployment process:

Workflow

  1. Trigger: Push to main or development branches

  2. Build Stage:

    • AWS credentials configuration
    • ECR (Elastic Container Registry) login
    • Branch to environment mapping (main β†’ prod, development β†’ dev)
    • Docker image build using Dockerfile.deploy
    • Tag with timestamp: {environment}-{YYYYMMDDTHHMMSSZ}
    • Push to ECR with versioned and latest tags
  3. Deploy Stage:

    • Lambda function update with new image
    • Wait for successful update confirmation
    • Publish new Lambda version

Environment Configuration

BRANCH_ENV_MAP: '{"main": "prod", "development": "dev", "staging": "stg", "testing": "tst"}'

Required Environment Variables

Secret Description
AWS_ACCESS_KEY_ID AWS access key
AWS_SECRET_ACCESS_KEY AWS secret key
AWS_DEFAULT_REGION AWS region (e.g., us-east-1)
ECR_REGISTRY ECR registry URI
ECR_REPO_NAME ECR repository name
AWS_LAMBDA_BASE_NAME Lambda function base name

Resource Naming Convention

  • ECR Images: {ECR_REGISTRY}/{ECR_REPO_NAME}:{environment}-{version}
  • Lambda Function: {AWS_LAMBDA_BASE_NAME}-{environment}

Example:

  • Image: 123456789.dkr.ecr.us-east-1.amazonaws.com/my-app:prod-20240115T143022Z
  • Lambda: my-lambda-function-prod

πŸ”§ Local Development

Prerequisites

  • Go 1.24+
  • Docker and Docker Compose

Setup

# Clone repository
git clone <repository-url>
cd aws-lambda-go

# Install dependencies
go mod download

# Run locally (HTTP mode)
go run cmd/main.go

Environment Variables

Variable Description Default
PORT Port for local HTTP mode 8080
ROOT_PATH API base path /
AWS_LAMBDA_FUNCTION_NAME Detects Lambda mode (auto) -

Testing with Docker

# Traditional HTTP API
docker-compose up app-api
curl http://localhost:9000/health

# Local Lambda emulation
docker-compose up app-lambda
curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" \
  -d '{"httpMethod":"GET", "path":"/health"}'

About

REST API built with Go & Gin Framework for AWS Lambda. Hybrid execution: Lambda functions in production, HTTP server for local development.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages