AWS Infrastructure with CDK
Production-ready AWS infrastructure using CDK — App Runner for HTTP services, Aurora Serverless for database, Cognito for auth, Lambda for serverless functions, and S3 for storage
Copies a short instruction with your private download link. Paste it into your agent: it downloads the whole skill (the source code, the setup steps and the mistakes we already made for you) and builds the screen.
Works with
What it does
Manually configuring AWS resources through the Console is error-prone, unrepeatable, and impossible to version control. As your app grows from a side project to a real product, you will inevitably need to reproduce your infrastructure for staging environments, disaster recovery, or a second project.
AWS CDK (Cloud Development Kit) solves this by letting you define your entire AWS infrastructure in TypeScript. You get:
- Reproducibility — One
cdk deploystands up your entire stack from scratch - Version control — Infrastructure changes go through the same PR review as application code
- Type safety — TypeScript catches misconfigurations at compile time, not at 3am in production
- Least-privilege by default — CDK's
grant*methods generate minimal IAM policies automatically - Cost visibility — All resources are declared in code, making it easy to audit what you are paying for
This skill provides a complete, production-tested CDK stack for indie iOS apps: App Runner for your HTTP backend, Aurora Serverless v2 for PostgreSQL, Cognito for authentication, Lambda for serverless tasks, and S3 for file storage.
What you get
- ✓1,068 lines of source code, ready to paste
- ✓Architecture
- ✓Dependencies
- ✓Implementation
- ✓Integration Checklist
- ✓Common Customizations
- ✓Known Pitfalls
Architecture
┌──────────────────────────────────────────────────────────────────────────┐
│ iOS Client │
└──────────────────────────────┬───────────────────────────────────────────┘
│
┌──────────────┼──────────────┐
│ │ │
▼ │ ▼
┌──────────────┐ │ ┌────────────────────┐
│ Cognito │ │ │ API Gateway │
│ User Pool │◄──────┼────│ (JWT Authorizer) │
│ + Hosted UI │ │ └─────────┬──────────┘
└──────┬───────┘ │ │
│ │ ▼
▼ │ ┌────────────────────┐
┌──────────────┐ │ │ App Runner │
│Identity Pool │◄──────┘ │ (Hono backend) │
│(anon + auth) │ └─────────┬──────────┘
└──────────────┘ │
┌─────────────┴─────────────┐
│ │
▼ ▼
┌──────────────┐ ┌────────────────┐
│ RDS Proxy │ │ S3 Bucket │
│ (conn pool) │ │ (file store) │
└──────┬───────┘ └────────────────┘
│
▼
┌──────────────┐
│ Aurora │
│Serverless v2 │
└──────────────┘Service roles:
| Service | Purpose | Why this one |
|---|---|---|
| Cognito User Pool | User authentication | Native Apple/Google sign-in, JWT auto-refresh |
| Cognito Identity Pool | Anonymous guests + credential management | Unique ID for every visitor, temporary AWS credentials |
| API Gateway HTTP API | API gateway | JWT Authorizer for unified auth, low latency |
| App Runner | Backend runtime | Auto-deploy from GitHub, auto-scale, zero server management |
| Aurora Serverless v2 | Database | Auto-scale, pay-per-use, PostgreSQL compatible |
| RDS Proxy | Connection pooling | Prevents connection storms, improves stability |
| Secrets Manager | Secret storage | Auto-rotation, secure database credential storage |
| S3 | File storage | Unlimited capacity, low cost |
| Lambda | Serverless functions | DB migrations, scheduled tasks, event processing |
| VPC | Network isolation | Private subnets protect your database |
Integration Checklist
1. AWS Account Setup
# Install AWS CLI
brew install awscli
# Configure a named profile
aws configure --profile wei
# Enter: Access Key ID, Secret Access Key, region (us-east-1), output (json)
# Verify access
aws sts get-caller-identity --profile wei2. CDK Bootstrap (one-time per account/region)
# Bootstrap CDK in your account
npx cdk bootstrap aws://ACCOUNT_ID/us-east-1 --profile wei3. GitHub Connection (for App Runner auto-deploy)
- Open AWS Console > App Runner > GitHub connections
- Click "Add new" and authorize your GitHub account
- Copy the Connection ARN into
cdk.json
4. First Deploy (two-step for social login)
# Step 1: Deploy without social login to create Secrets
# Set in cdk.json: "enableSocialLogin": "false"
npm run cdk:deploy
# Step 2: Update secrets with real values
aws secretsmanager put-secret-value \
--secret-id my-app/secrets \
--secret-string '{"AUTH_APPLE_PRIVATE_KEY":"...","AUTH_GOOGLE_CLIENT_SECRET":"..."}' \
--profile wei --region us-east-1
# Step 3: Enable social login and redeploy
# Set in cdk.json: "enableSocialLogin": "true"
npm run cdk:deploy5. Subsequent Deploys
# Preview changes
npm run cdk:diff
# Deploy
npm run cdk:deployKnown Pitfalls
1. App Runner Cannot Reach RDS Proxy
Symptom: Connection timeout or refused.
Cause: Security group rules are missing or misconfigured.
Fix: Ensure the RDS Proxy security group allows inbound traffic from the App Runner security group on port 5432.
proxySecurityGroup.addIngressRule(
appRunnerSecurityGroup,
ec2.Port.tcp(5432),
"Allow App Runner to connect to RDS Proxy"
);2. Lambda Cannot Access Secrets Manager
Symptom: getaddrinfo ENOTFOUND secretsmanager.*.amazonaws.com
Cause: Lambda is in a private subnet with no route to AWS service endpoints.
Fix: Add a VPC endpoint for Secrets Manager, or ensure your VPC has a NAT Gateway.
vpc.addInterfaceEndpoint("SecretsManagerEndpoint", {
service: ec2.InterfaceVpcEndpointAwsService.SECRETS_MANAGER,
});3. CDK Deploy Stuck on Custom Resource
Symptom: Deployment hangs at CREATE_IN_PROGRESS for 60+ minutes.
Cause: The Lambda backing the Custom Resource timed out (network issues) or threw an unhandled error (no CloudFormation response sent).
Fix:
- Check Lambda logs in CloudWatch > Log groups
- Verify the Lambda has correct VPC/subnet/security-group configuration
- Increase Lambda timeout
- Ensure the migration code properly handles CloudFormation callback responses
4. Cognito signInAliases Cannot Be Modified
Symptom: Updates are not allowed for property - UsernameAttributes
Cause: Cognito User Pool signInAliases and passwordPolicy are immutable after creation.
Fix: Delete and recreate the User Pool, or plan your initial configuration carefully to include all login methods you might need.
5. App Runner Build Fails
Symptom: Build failed in App Runner deployment logs.
Common causes:
- Missing
--include=devin build command (TypeScript compiler is a devDependency) - Wrong Node.js runtime version
- Missing environment variables during build
Fix:
codeConfigurationValues: {
runtime: apprunner.Runtime.NODEJS_22,
buildCommand: "npm ci --include=dev && npm run build",
startCommand: "node dist/src/server.js",
}6. TypeScript Path Aliases Fail in Production
Symptom: Works locally, but Cannot find module '@/xxx' after deployment.
Cause: TypeScript paths are only for type checking. esbuild and the Node.js runtime do not resolve them.
Fix: Use relative imports instead of path aliases.
// Avoid
import { db } from "@/db/client";
// Use relative paths
import { db } from "../db/client";7. CDK Overwrites Your Real Secrets
Symptom: After cdk deploy, your API keys in Secrets Manager are replaced with "PLACEHOLDER".
Cause: You changed the placeholder text in your CDK code, so CDK detected a diff and applied it.
Fix: Never modify the placeholder values in secretObjectValue after the initial deploy. The placeholders exist only so CDK can create the secret — you update real values manually via the AWS CLI.
8. API Gateway Returns 401
Symptom: Authenticated requests are rejected.
Checklist:
- Is the JWT token expired? (default: 1 hour)
- Does the token come from the correct User Pool?
- Does
jwtAudiencematch the App Client ID? - Is the header format correct?
Authorization: Bearer <token>
9. Cost Awareness
| Resource | Dev Config | Estimated Monthly Cost |
|---|---|---|
| NAT Gateway | 1 instance | ~$32 |
| Aurora Serverless v2 | 0.5-2 ACU | ~$43 |
| App Runner | 0.25 vCPU / 0.5 GB | ~$5 |
| Secrets Manager | 1 secret | ~$0.40 |
| Total | ~$80/mo |
To minimize cost during development:
natGateways: 1, // Single NAT Gateway
serverlessV2MinCapacity: 0.5, // Minimum Aurora capacity
cpu: apprunner.Cpu.QUARTER_VCPU, // Smallest App Runner
memory: apprunner.Memory.HALF_GB,
removalPolicy: cdk.RemovalPolicy.DESTROY, // Easy cleanup
deletionProtection: false,Production Readiness Checklist
deletionProtection: trueon Aurora and CognitoremovalPolicy: cdk.RemovalPolicy.RETAINon database and secrets- Multi-AZ deployment:
natGateways: 2 - Aurora backup:
retention: cdk.Duration.days(30) - CloudWatch alarms for error rates and latency
- VPC Flow Logs enabled
- Secrets rotation configured
enableDataApi: falseon Aurora cluster
More in Backend
- Supabase Row Level Security (RLS) Data IsolationFreeProduction-ready Row Level Security patterns for a Supabase + Cloudflare Workers stack — the database-layer isolation primitive that makes cross-user data leaks impossible even when a route is buggy or someone talks to PostgREST directly. Covers the owner-isolation table skeleton (1:1 profile + one-to-many children), the five load-bearing RLS rules ((select auth.uid()) wrapping, TO authenticated, forward-set child subqueries, indexing policy columns, trusting only auth.uid()/app_metadata), the service_role (bypass RLS) vs user-JWT (RLS enforced) client split, server-side owner_id injection, public read-only tables whose writes funnel through service_role, and a two-token cross-access isolation test that must return 403/empty.
- Supabase Storage Private Buckets + Signed URLsFreeProduction-ready private file storage for a Supabase + Cloudflare Workers stack — a private bucket created as an idempotent SQL migration, server-side service_role uploads (the client never talks to Storage and never holds the service key), and on-demand signed URLs with short TTLs. IDOR-proof by construction — the object path is derived from the verified JWT ({userId}/{itemId}.jpg, never client-supplied) and ownership is checked through RLS before any URL is signed — with defense-in-depth RLS policies on storage.objects, orphan-object cleanup (Postgres ON DELETE CASCADE never touches Storage), and the pitfalls of public buckets and leaked signed URLs.
- Supabase + Cloudflare Workers InfrastructureFreeProduction-ready serverless infrastructure using Supabase (managed Postgres + Auth + Storage) and Cloudflare Workers (Hono backend) — the zero-VPC, zero-server counterpart to the AWS CDK stack. Covers Supabase CLI migrations, the immutable-region rule, secret management (.dev.vars / Cloudflare Secret, never git), Workers deployment via wrangler, and dual-mode JWT verification (JWKS ES256 preferred, HS256 fallback).
- TikTok Ad Attribution with RevenueCat (Dual-Channel)ProFull-depth TikTok attribution for RevenueCat apps — client SDK reports the purchase moment, your backend forwards renewals/refunds from RC webhooks to TikTok Events API. Ships the complete Workers forwarder, device-identifier relay chain (ATT → RC → webhook → Events API), ASN wiring, and a battle-tested verification SOP (iOS + Cloudflare Workers)
- TikTok Ad Attribution & TrackingProTikTok ad attribution module with ATT permission flow, standard/custom event tracking, and debug logging — bridges to TikTok Business SDK via closure injection. Includes complete integration guide with credentials setup, App Store compliance, and campaign launch workflow (iOS only)