Swift Lambda Sample
Summary
This repository demonstrates how to build and deploy a complete serverless application using Swift on AWS Lambda. It showcases best practices for Swift server development and provides a foundation for building scalable serverless APIs.
The project demonstrates integration with various AWS services:
- API Gateway - HTTP/S REST API endpoints
- CloudWatch - Scheduled invocations and logging
- DynamoDB - DynamoDB for storgage
- Lambda - Swift-based serverless compute
- RDS (PostgreSQL) - Managed relational database with SSL/TLS
- S3 - Object storage for file operations
- Secrets Manager - Secure credential storage
- SQS - Message queuing with Dead Letter Queue
- VPC - Network isolation with public/private subnets
The repository includes infrastructure-as-code using AWS CDK (TypeScript), automated CI/CD via GitHub Actions, and a custom CLIApp CLI and Mac app for streamlined deployment management.
Deployment
This project uses the CLIApp CLI tool for managing AWS deployments. The tool handles both infrastructure (via CDK) and Lambda code deployment.
Prerequisites: Configure AWS credentials and profile (see CLAUDE.md for details)
Using aws-vault (MFA)
If you use aws-vault with MFA, create a wrapper script and credential_process profile:
# 1. Create wrapper script
cat > ~/.swiftSampleDemo/aws-vault-export.sh << 'EOF'
#!/bin/bash
export HOME=/Users/<your-username>
export AWS_VAULT_KEYCHAIN_NAME=login
exec /opt/homebrew/bin/aws-vault export --format=json <your-profile>
EOF
chmod +x ~/.swiftSampleDemo/aws-vault-export.sh
# 2. Add to ~/.aws/config
echo '[profile <your-profile>-vault]
region = us-east-1
credential_process = /Users/<your-username>/.swiftSampleDemo/aws-vault-export.sh' >> ~/.aws/config
# 3. Configure app to use the new profile
echo '{"profileName": "<your-profile>-vault", "useAWSVault": false}' > ~/.swiftSampleDemo/aws-config.jsonBefore using the CLI or Mac app, prime your session:
aws-vault exec <your-profile> -- echo "Session primed"Quick Start
Deploy with minimal AWS costs (no database, no NAT Gateway):
# Initial deployment
swift run CLIApp aws deploy-init
# Or using the tools.sh wrapper
./tools.sh aws deploy-initDeployment Commands
| Command | Description |
|---|---|
swift run CLIApp aws deploy-init |
Initial deployment: CDK infrastructure + Lambda code |
swift run CLIApp aws deploy |
Update CDK infrastructure only |
swift run CLIApp aws update-lambda |
Update Lambda code only |
swift run CLIApp aws status |
Check deployment status and outputs |
swift run CLIApp aws tear-down |
Destroy all infrastructure |
swift run CLIApp aws test all |
Test deployed endpoints |
swift run CLIApp aws logs |
Show CloudWatch logs |
Deployment Options
Control costs by choosing which resources to deploy:
# Minimal deployment (default: no Postgres, no NAT Gateway)
swift run CLIApp aws deploy-init
# Include PostgreSQL database (~$15/month)
swift run CLIApp aws deploy-init --with-postgres
# Full deployment with PostgreSQL and NAT Gateway (~$47/month)
swift run CLIApp aws deploy-init --with-postgres --with-nat-gatewayUsing tools.sh Wrapper
The tools.sh script is a thin wrapper that delegates to CLIApp:
# Deployment
./tools.sh aws deploy-init # Initial deployment
./tools.sh aws deploy-init --with-postgres # With database
./tools.sh aws deploy # Update infrastructure
./tools.sh aws update-lambda # Update Lambda code
./tools.sh aws status # Check status
./tools.sh aws tear-down # Destroy everything
# Testing
./tools.sh aws test all # Run all tests
./tools.sh aws test s3-upload # Test S3 endpoint
./tools.sh aws logs # View logsTypical Workflows
Initial Setup:
./tools.sh aws deploy-init
./tools.sh aws test allUpdate Lambda Code:
# Make changes to Swift code
vim Sources/apps/LambdaApp/Handlers/APIGatewayHandler.swift
# Commit and deploy
git add -A && git commit -m "Update handler"
./tools.sh aws update-lambdaUpdate Infrastructure:
# Modify CDK code
vim cdk/lib/constructs/lambda-construct.ts
# Deploy infrastructure changes
./tools.sh aws deployFor detailed deployment documentation, see CLAUDE.md.
Local Development
There are three ways to run and test the Lambda locally:
- Native Mac via Xcode - Best for active development and debugging
- Build for Linux - Create deployment packages for AWS
- Linux Container - Test in production-like environment
Quick Start
For daily development (Mac/Xcode):
# Start local services
./tools.sh local copy-config
./tools.sh local services start-all
# Then run in Xcode (⌘R)For testing before AWS deployment:
# Build and test in Linux container
./build.sh LambdaApp
./tools.sh local services start-all
./tools.sh local linux run-interactive
# Inside container:
./bootstrapFull Documentation
For complete instructions on all three approaches, see Running Locally Guide
The guide covers:
- Xcode setup and configuration
- Building for Linux/AWS Lambda
- Running in Docker containers
- Database and S3 access
- Troubleshooting common issues
- Switching between local and remote AWS services
Connecting to Remote AWS Services
While local services are recommended for development, you can connect to remote AWS services when needed. See the Running Locally Guide for details on:
- AWS CLI configuration
- Updating local configuration for remote endpoints
- Security considerations
GitHub Actions Setup
This section walks you through configuring GitHub Actions for automated Lambda deployment.
Step 1: Update Workflow Variables
File: .github/workflows/deploy_dev.yml
Update these variables to match your project:
productName: LambdaApp # Your Swift Package product name
lambdaName: swift-lambda-sample # Your Lambda function nameStep 2: Enable Actions Permissions
- Go to your GitHub repo → Settings
- Click Actions → General in the left sidebar
- Scroll to "Workflow permissions"
- Select "Read and write permissions"
- Click Save
Step 3: Create the Environment
- Go to Settings → Environments
- Click "New Environment"
- Name:
dev - Click "Configure environment"
Step 4: Configure Deployment Branches
Inside the dev environment:
- Find "Deployment branches and tags"
- Click dropdown → Select "Selected branches and tags"
- Click "Add deployment branch or tag rule"
- Type
devand click Add rule
Step 5: Add Environment Secret (AWS_ROLE_ARN)
Inside the dev environment → "Environment secrets":
- Click "Add secret"
- Name:
AWS_ROLE_ARN - Value: Your AWS OIDC Role ARN (e.g.,
arn:aws:iam::123456789012:role/YourRole_GitHub_OIDC_Role)
See Creating an AWS OIDC Role below if you need to create one.
Step 6: Add Environment Variable (AWS_REGION)
Inside the dev environment → "Environment variables":
- Click "Add variable"
- Name:
AWS_REGION - Value:
us-east-1
Step 7: Add Repository Secret (PAT)
- Go to Settings → Secrets and variables → Actions
- Click "New repository secret"
- Name:
SWIFT_PACKAGE_MANAGER_PAT - Value: A GitHub Personal Access Token with
read:packagespermission
Creating an AWS OIDC Role
If you don't have an OIDC role for GitHub Actions, create one using the AWS CLI:
1. Create the trust policy file:
cat << 'EOF' > /tmp/trust-policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::<AWS_ACCOUNT_ID>:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:<GITHUB_ORG>/<REPO_NAME>:*"
}
}
}
]
}
EOFReplace <AWS_ACCOUNT_ID>, <GITHUB_ORG>, and <REPO_NAME> with your values.
2. Create the IAM role:
aws iam create-role \
--role-name <YourProject>_GitHub_OIDC_Role \
--assume-role-policy-document file:///tmp/trust-policy.json \
--description "GitHub Actions OIDC role for <repo-name>"3. Attach Lambda deployment permissions:
cat << 'EOF' > /tmp/lambda-deploy-policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"lambda:UpdateFunctionCode",
"lambda:GetFunction"
],
"Resource": "arn:aws:lambda:us-east-1:<AWS_ACCOUNT_ID>:function:<lambda-function-name>"
}
]
}
EOF
aws iam put-role-policy \
--role-name <YourProject>_GitHub_OIDC_Role \
--policy-name LambdaDeployPolicy \
--policy-document file:///tmp/lambda-deploy-policy.json4. Get the Role ARN:
aws iam get-role --role-name <YourProject>_GitHub_OIDC_Role --query "Role.Arn" --output textUse this ARN for the AWS_ROLE_ARN environment secret in Step 5.
Troubleshooting
Accessing Local Postgres Database
It may be useful to login to the local postgres instance for viewing schemas and data.
- Get Container ID: Retrieve with
docker ps -a. - Docker Access: Gain access using
docker exec -it <container id> /bin/bash. - Run Postgres Commands: Start with
psqland then utilize commands to interact with databases and tables.- List all databases: \l
- Connect to docker database: \c docker
- List all tables: \dt
Additional Documentation
- Running Locally Guide - Comprehensive local development guide
- CLAUDE.md - Detailed deployment and AWS operations guide
- PRINCIPLES.md - Server development principles and best practices
- TODO.md - Project roadmap and planned improvements