CI/CD for a Serverless API on AWS Lambda with GitHub Actions
A serverless API tests its handlers locally, then deploys to AWS Lambda with OIDC - no stored AWS keys.
This recipe deploys a serverless API to AWS Lambda from GitHub Actions. It tests handler logic, then deploys with the Serverless Framework (or SAM) using OIDC role assumption on main.
What the pipeline does
- npm ci
- Lint
- Test handlers (mock or local DB)
- Assume an AWS role via OIDC
- Deploy the stack on main
The workflow
OIDC gives the deploy job short-lived AWS credentials instead of long-lived secrets.
name: Deploy
on: [push, pull_request]
permissions:
id-token: write
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: npm
- run: npm ci
- run: npm run lint
- run: npm test
deploy:
needs: test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: npm
- run: npm ci
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
- run: npx serverless deploy --stage prodTesting and dependencies
Handler logic is plain functions, so unit-test them directly with mocked events. For integration tests against a database, add a Postgres service container and point the handler at DATABASE_URL, or use the local emulator (serverless-offline / sam local). Keep DynamoDB access tested with a local DynamoDB container if your API uses it.
Deploying
serverless deploy (or sam deploy) packages the functions and rolls the CloudFormation stack - API Gateway, Lambda, and IAM in one shot. Use stages (--stage prod) to separate environments. The OIDC role needs permissions for CloudFormation, Lambda, API Gateway, and IAM. Managed runners that auto-retry transient AWS API and registry flakes keep deploys green, at about 69% less than GitHub-hosted runners.
Key takeaways
- Unit-test Lambda handlers directly with mocked events - they are plain functions.
- Deploy with OIDC role assumption instead of stored AWS keys.
- serverless/sam deploy rolls API Gateway, Lambda, and IAM as one stack.