@thunder-so/thunder 1.3.0 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,178 @@
1
+ # Deploy a Serverless API with AWS Lambda and API Gateway
2
+
3
+ Run your Node.js backend on [AWS Lambda](https://aws.amazon.com/lambda/) with an [API Gateway HTTP API](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api.html) as the public endpoint. No servers to provision, scales to zero when idle, and pay only for what you use.
4
+
5
+ Works with any framework that exports a Lambda handler: [Express.js](https://expressjs.com/), [Hono](https://hono.dev/), [Fastify](https://fastify.dev/), [NestJS](https://nestjs.com/), [Koa](https://koajs.com/), and more.
6
+
7
+ ## AWS Resources
8
+
9
+ | Resource | Purpose |
10
+ |---|---|
11
+ | [Lambda Function](https://aws.amazon.com/lambda/) | Runs your server code |
12
+ | [API Gateway HTTP API](https://aws.amazon.com/api-gateway/) | Public HTTP endpoint, routes all traffic to Lambda |
13
+ | [CloudWatch Logs](https://aws.amazon.com/cloudwatch/) | Function logs, retained for 1 month |
14
+ | [ACM Certificate](https://aws.amazon.com/certificate-manager/) | SSL for custom domain (optional) |
15
+ | [Route53](https://aws.amazon.com/route53/) | DNS A + AAAA records (optional) |
16
+
17
+ ## Prerequisites
18
+
19
+ - [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) configured
20
+ - [AWS CDK](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html) bootstrapped:
21
+ ```bash
22
+ cdk bootstrap aws://YOUR_ACCOUNT_ID/us-east-1
23
+ ```
24
+ - Your function built to a `dist/` directory with an `index.handler` export
25
+
26
+ ## Installation
27
+
28
+ ```bash
29
+ bun add @thunder-so/thunder --development
30
+ # or
31
+ npm install @thunder-so/thunder --save-dev
32
+ ```
33
+
34
+ ## Handler Requirements
35
+
36
+ Your Lambda handler must follow the [AWS Lambda handler signature](https://docs.aws.amazon.com/lambda/latest/dg/nodejs-handler.html). For HTTP APIs, use the v2 payload format:
37
+
38
+ ```typescript
39
+ // src/index.ts
40
+ export const handler = async (event: any) => {
41
+ return {
42
+ statusCode: 200,
43
+ body: JSON.stringify({ message: 'Hello from Lambda' }),
44
+ };
45
+ };
46
+ ```
47
+
48
+ For Express/Hono/Fastify, use an adapter like [`@hono/node-server`](https://hono.dev/docs/getting-started/aws-lambda) or [`serverless-http`](https://github.com/dougmoscrop/serverless-http):
49
+
50
+ ```typescript
51
+ // src/index.ts (Hono example)
52
+ import { Hono } from 'hono';
53
+ import { handle } from 'hono/aws-lambda';
54
+
55
+ const app = new Hono();
56
+ app.get('/', (c) => c.json({ message: 'Hello' }));
57
+
58
+ export const handler = handle(app);
59
+ ```
60
+
61
+ ## Stack File
62
+
63
+ ```typescript
64
+ import { Cdk, Lambda, type LambdaProps } from '@thunder-so/thunder';
65
+
66
+ const config: LambdaProps = {
67
+ env: {
68
+ account: '123456789012',
69
+ region: 'us-east-1',
70
+ },
71
+ application: 'myapp',
72
+ service: 'api',
73
+ environment: 'dev',
74
+
75
+ rootDir: '.',
76
+
77
+ functionProps: {
78
+ runtime: Cdk.aws_lambda.Runtime.NODEJS_22_X,
79
+ architecture: Cdk.aws_lambda.Architecture.ARM_64,
80
+ codeDir: 'dist', // directory containing your built handler
81
+ handler: 'index.handler',
82
+ memorySize: 512,
83
+ timeout: 10,
84
+ },
85
+ };
86
+
87
+ new Lambda(
88
+ new Cdk.App(),
89
+ `${config.application}-${config.service}-${config.environment}-stack`,
90
+ config
91
+ );
92
+ ```
93
+
94
+ ## Deploy
95
+
96
+ ```bash
97
+ # Build your app first
98
+ npm run build
99
+
100
+ # Deploy
101
+ npx cdk deploy --app "npx tsx stack/dev.ts" --profile default
102
+ ```
103
+
104
+ CDK outputs the API Gateway URL:
105
+
106
+ ```
107
+ Outputs:
108
+ myapp-api-dev-stack.ApiGatewayUrl = https://abc123.execute-api.us-east-1.amazonaws.com
109
+ ```
110
+
111
+ ## Custom Domain (Optional)
112
+
113
+ 1. [Create a Route53 Hosted Zone](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/AboutHZWorkingWith.html)
114
+ 2. [Request an ACM certificate](https://docs.aws.amazon.com/acm/latest/userguide/gs-acm-request-public.html) in the **same region as your function**
115
+
116
+ ```typescript
117
+ const config: LambdaProps = {
118
+ // ...
119
+ domain: 'api.example.com',
120
+ regionalCertificateArn: 'arn:aws:acm:us-east-1:123456789012:certificate/abc-123',
121
+ hostedZoneId: 'Z1234567890ABC',
122
+ };
123
+ ```
124
+
125
+ > Unlike Static, the Lambda certificate must be **regional** (same region as the function), not global.
126
+
127
+ ## Environment Variables
128
+
129
+ ```typescript
130
+ functionProps: {
131
+ // ...
132
+ variables: [
133
+ { NODE_ENV: 'production' },
134
+ { API_BASE_URL: 'https://api.example.com' },
135
+ ],
136
+ },
137
+ ```
138
+
139
+ ## Secrets from AWS Secrets Manager
140
+
141
+ Store sensitive values in [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/) and inject them as environment variables at deploy time:
142
+
143
+ ```bash
144
+ aws secretsmanager create-secret \
145
+ --name "/myapp/DATABASE_URL" \
146
+ --secret-string "postgres://user:pass@host/db"
147
+ ```
148
+
149
+ ```typescript
150
+ functionProps: {
151
+ // ...
152
+ secrets: [
153
+ { key: 'DATABASE_URL', resource: 'arn:aws:secretsmanager:us-east-1:123456789012:secret:/myapp/DATABASE_URL-abc123' },
154
+ ],
155
+ },
156
+ ```
157
+
158
+ Thunder automatically grants the Lambda execution role `secretsmanager:GetSecretValue` on each secret.
159
+
160
+ ## Stack Outputs
161
+
162
+ | Output | Description |
163
+ |---|---|
164
+ | `ApiGatewayUrl` | API Gateway endpoint URL |
165
+ | `LambdaFunction` | Lambda function name |
166
+ | `LambdaFunctionUrl` | Direct Lambda URL (only if `url: true`) |
167
+ | `Route53Domain` | Custom domain URL (only if domain is configured) |
168
+
169
+ ## Destroy
170
+
171
+ ```bash
172
+ npx cdk destroy --app "npx tsx stack/dev.ts" --profile default
173
+ ```
174
+
175
+ ## Next Steps
176
+
177
+ - [lambda-containers.md](./lambda-containers.md) - Container images and Bun runtime with Lambda
178
+ - [lambda-full.md](./lambda-full.md) - Full configuration reference
@@ -0,0 +1,179 @@
1
+ # AWS Lambda with Container Images and Bun Runtime
2
+
3
+ Deploy AWS Lambda functions as Docker container images to use custom runtimes like [Bun](https://bun.sh/), ship larger packages, or include system-level dependencies. Thunder builds and pushes the image to [Amazon ECR](https://aws.amazon.com/ecr/) automatically during `cdk deploy`.
4
+
5
+ Use container Lambda when:
6
+
7
+ - Your deployment package exceeds the 250 MB zip limit
8
+ - You need a runtime not natively supported by Lambda (e.g. Bun)
9
+ - You want to use system-level dependencies (native modules, binaries)
10
+ ## Node.js Container Image
11
+
12
+ ### 1. Create a Dockerfile
13
+
14
+ ```dockerfile
15
+ # Dockerfile
16
+ FROM public.ecr.aws/lambda/nodejs:22 AS builder
17
+ WORKDIR ${LAMBDA_TASK_ROOT}
18
+
19
+ COPY . .
20
+ RUN npm ci
21
+ RUN npm run build
22
+
23
+ FROM public.ecr.aws/lambda/nodejs:22
24
+ WORKDIR ${LAMBDA_TASK_ROOT}
25
+
26
+ COPY --from=builder /var/task/dist/ ./
27
+ COPY --from=builder /var/task/node_modules ./node_modules
28
+
29
+ CMD ["index.handler"]
30
+ ```
31
+
32
+ ### 2. Stack File
33
+
34
+ ```typescript
35
+ import { Cdk, Lambda, type LambdaProps } from '@thunder-so/thunder';
36
+
37
+ const config: LambdaProps = {
38
+ env: { account: '123456789012', region: 'us-east-1' },
39
+ application: 'myapp',
40
+ service: 'api',
41
+ environment: 'prod',
42
+ rootDir: '.',
43
+
44
+ functionProps: {
45
+ dockerFile: 'Dockerfile', // path relative to rootDir
46
+ memorySize: 1792,
47
+ timeout: 10,
48
+ variables: [{ NODE_ENV: 'production' }],
49
+ },
50
+ };
51
+
52
+ new Lambda(new Cdk.App(), 'myapp-api-prod-stack', config);
53
+ ```
54
+
55
+ > When `dockerFile` is set, `runtime`, `architecture`, `codeDir`, `handler`, `include`, and `exclude` are ignored - the Dockerfile controls the build entirely.
56
+
57
+ ## Bun Runtime Container Image
58
+
59
+ [Bun](https://bun.sh/) is a fast JavaScript runtime with native TypeScript support. Since Lambda doesn't have a managed Bun runtime, you deploy it as a container.
60
+
61
+ ### 1. Create the Bun Dockerfile
62
+
63
+ ```dockerfile
64
+ # Dockerfile.bun
65
+
66
+ # Stage 1: Build the Bun Lambda bootstrap
67
+ FROM oven/bun:latest AS bun
68
+ WORKDIR /tmp
69
+ RUN apt-get update && apt-get install -y curl
70
+ RUN curl -fsSL https://raw.githubusercontent.com/oven-sh/bun/main/packages/bun-lambda/runtime.ts -o runtime.ts
71
+ RUN bun install aws4fetch
72
+ RUN bun build --compile runtime.ts --outfile bootstrap
73
+
74
+ # Stage 2: Build your app
75
+ FROM oven/bun:latest AS builder
76
+ WORKDIR /tmp
77
+ COPY . .
78
+ RUN bun install
79
+ RUN bun run build
80
+
81
+ # Stage 3: Runtime image
82
+ FROM public.ecr.aws/lambda/provided:al2023
83
+ WORKDIR ${LAMBDA_TASK_ROOT}
84
+
85
+ COPY --from=bun /usr/local/bin/bun /opt/bun
86
+ COPY --from=bun /tmp/bootstrap ${LAMBDA_RUNTIME_DIR}
87
+ COPY --from=builder /tmp/dist/ ./
88
+ COPY --from=builder /tmp/node_modules ./node_modules
89
+ COPY --from=builder /tmp/lambda-bun.js ./lambda-bun.js
90
+
91
+ CMD ["lambda-bun.fetch"]
92
+ ```
93
+
94
+ ### 2. Create the Bun Handler Shim
95
+
96
+ Bun's Lambda runtime expects a `fetch`-compatible handler:
97
+
98
+ ```javascript
99
+ // lambda-bun.js
100
+ const { handler } = require('./index.js');
101
+ exports.fetch = handler;
102
+ ```
103
+
104
+ If you're using [Hono](https://hono.dev/) with Bun:
105
+
106
+ ```typescript
107
+ // src/index.ts
108
+ import { Hono } from 'hono';
109
+
110
+ const app = new Hono();
111
+ app.get('/', (c) => c.json({ ok: true }));
112
+
113
+ export default app; // Bun's fetch handler
114
+ ```
115
+
116
+ ### 3. Stack File
117
+
118
+ ```typescript
119
+ import { Cdk, Lambda, type LambdaProps } from '@thunder-so/thunder';
120
+
121
+ const config: LambdaProps = {
122
+ env: { account: '123456789012', region: 'us-east-1' },
123
+ application: 'myapp',
124
+ service: 'api',
125
+ environment: 'prod',
126
+ rootDir: '.',
127
+
128
+ functionProps: {
129
+ dockerFile: 'Dockerfile.bun',
130
+ memorySize: 512,
131
+ timeout: 10,
132
+ keepWarm: true,
133
+ },
134
+ };
135
+
136
+ new Lambda(new Cdk.App(), 'myapp-api-prod-stack', config);
137
+ ```
138
+
139
+ ## Docker Build Arguments
140
+
141
+ Pass build-time arguments to your Dockerfile:
142
+
143
+ ```typescript
144
+ functionProps: {
145
+ dockerFile: 'Dockerfile',
146
+ dockerBuildArgs: ['NODE_ENV=production', 'APP_VERSION=1.2.3'],
147
+ },
148
+ ```
149
+
150
+ In your Dockerfile:
151
+ ```dockerfile
152
+ ARG NODE_ENV
153
+ ARG APP_VERSION
154
+ ```
155
+
156
+ ## Keep Warm
157
+
158
+ Container Lambdas have longer cold starts than zip-based functions. Use `keepWarm` to schedule an EventBridge ping every 5 minutes:
159
+
160
+ ```typescript
161
+ functionProps: {
162
+ dockerFile: 'Dockerfile',
163
+ keepWarm: true,
164
+ },
165
+ ```
166
+
167
+ This creates an [EventBridge rule](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-rules.html) that invokes the function with a synthetic API Gateway v2 event.
168
+
169
+ ## Notes on Container Deployments
170
+
171
+ - The entire `rootDir` is used as the Docker build context
172
+ - CDK builds and pushes the image to [Amazon ECR](https://aws.amazon.com/ecr/) automatically during `cdk deploy`
173
+ - `timeout`, `memorySize`, `tracing`, `keepWarm`, `variables`, and `secrets` all work the same as zip deployments
174
+ - For more on Lambda container images, see the [AWS docs](https://docs.aws.amazon.com/lambda/latest/dg/images-create.html)
175
+
176
+ ## Related
177
+
178
+ - [lambda-basic.md](./lambda-basic.md) - Basic zip-based deployment
179
+ - [lambda-full.md](./lambda-full.md) - Full configuration reference
@@ -0,0 +1,185 @@
1
+ # Lambda Configuration Reference
2
+
3
+ Complete reference for every option available in the `Lambda` construct. Covers zip and container deployments, concurrency, secrets, custom domains, and VPC integration. For a quick start see [lambda-basic.md](./lambda-basic.md).
4
+
5
+ ## Examples
6
+
7
+ ### Zip Deployment
8
+
9
+ ```typescript
10
+ import { Cdk, Lambda, type LambdaProps } from '@thunder-so/thunder';
11
+
12
+ const config: LambdaProps = {
13
+ env: { account: '123456789012', region: 'us-east-1' },
14
+ application: 'myapp',
15
+ service: 'api',
16
+ environment: 'prod',
17
+ rootDir: '.',
18
+
19
+ domain: 'api.example.com',
20
+ regionalCertificateArn: 'arn:aws:acm:us-east-1:123456789012:certificate/abc-123',
21
+ hostedZoneId: 'Z1234567890ABC',
22
+
23
+ functionProps: {
24
+ runtime: Cdk.aws_lambda.Runtime.NODEJS_22_X,
25
+ architecture: Cdk.aws_lambda.Architecture.ARM_64,
26
+ codeDir: 'dist',
27
+ handler: 'index.handler',
28
+ include: ['package.json'],
29
+ exclude: ['**/*.test.js'],
30
+ memorySize: 1792,
31
+ timeout: 10,
32
+ tracing: true,
33
+ reservedConcurrency: 10,
34
+ provisionedConcurrency: 2,
35
+ keepWarm: true,
36
+ url: true,
37
+ variables: [
38
+ { NODE_ENV: 'production' },
39
+ { LOG_LEVEL: 'info' },
40
+ ],
41
+ secrets: [
42
+ { key: 'DATABASE_URL', resource: 'arn:aws:secretsmanager:us-east-1:123456789012:secret:/myapp/db-abc123' },
43
+ ],
44
+ },
45
+ };
46
+
47
+ new Lambda(new Cdk.App(), 'myapp-api-prod-stack', config);
48
+ ```
49
+
50
+ ### Container Deployment
51
+
52
+ ```typescript
53
+ import { Cdk, Lambda, type LambdaProps } from '@thunder-so/thunder';
54
+
55
+ const config: LambdaProps = {
56
+ env: { account: '123456789012', region: 'us-east-1' },
57
+ application: 'myapp',
58
+ service: 'api',
59
+ environment: 'prod',
60
+ rootDir: '.',
61
+
62
+ domain: 'api.example.com',
63
+ regionalCertificateArn: 'arn:aws:acm:us-east-1:123456789012:certificate/abc-123',
64
+ hostedZoneId: 'Z1234567890ABC',
65
+
66
+ functionProps: {
67
+ dockerFile: 'Dockerfile',
68
+ dockerBuildArgs: ['NODE_ENV=production'],
69
+ memorySize: 1792,
70
+ timeout: 10,
71
+ tracing: true,
72
+ keepWarm: true,
73
+ variables: [
74
+ { NODE_ENV: 'production' },
75
+ ],
76
+ secrets: [
77
+ { key: 'DATABASE_URL', resource: 'arn:aws:secretsmanager:us-east-1:123456789012:secret:/myapp/db-abc123' },
78
+ ],
79
+ },
80
+ };
81
+
82
+ new Lambda(new Cdk.App(), 'myapp-api-prod-stack', config);
83
+ ```
84
+
85
+ ## Property Reference
86
+
87
+ ### Identity (`AppProps`)
88
+
89
+ | Property | Type | Required | Default | Description |
90
+ |---|---|---|---|---|
91
+ | `env.account` | `string` | Yes | - | AWS account ID |
92
+ | `env.region` | `string` | Yes | - | AWS region |
93
+ | `application` | `string` | Yes | - | Project name |
94
+ | `service` | `string` | Yes | - | Service name |
95
+ | `environment` | `string` | Yes | - | Environment label |
96
+ | `rootDir` | `string` | No | `.` | Root of your app |
97
+ | `debug` | `boolean` | No | `false` | Enable verbose logging |
98
+
99
+ ### Custom Domain
100
+
101
+ | Property | Type | Description |
102
+ |---|---|---|
103
+ | `domain` | `string` | Public domain, e.g. `api.example.com` |
104
+ | `regionalCertificateArn` | `string` | ACM certificate ARN - **must be in the same region as the function** |
105
+ | `hostedZoneId` | `string` | Route53 hosted zone ID |
106
+
107
+ ### `functionProps`
108
+
109
+ #### Zip Deployment
110
+
111
+ | Property | Type | Default | Description |
112
+ |---|---|---|---|
113
+ | `runtime` | `Runtime` | `NODEJS_20_X` | Lambda runtime. See [supported runtimes](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html) |
114
+ | `architecture` | `Architecture` | `ARM_64` | `ARM_64` or `X86_64`. ARM is cheaper and often faster |
115
+ | `codeDir` | `string` | `.` | Directory containing your built handler, relative to `rootDir` |
116
+ | `handler` | `string` | `index.handler` | Handler in `file.export` format |
117
+ | `include` | `string[]` | - | Extra files to copy into `codeDir` before packaging (e.g. `package.json`) |
118
+ | `exclude` | `string[]` | `['**/*.svg', '**/*.map', ...]` | Glob patterns to exclude from the zip |
119
+
120
+ #### Container Deployment
121
+
122
+ | Property | Type | Description |
123
+ |---|---|---|
124
+ | `dockerFile` | `string` | Path to Dockerfile relative to `rootDir`. Enables container mode. |
125
+ | `dockerBuildArgs` | `string[]` | Build args in `KEY=VALUE` format, passed to `docker build --build-arg` |
126
+
127
+ When `dockerFile` is set, `runtime`, `architecture`, `codeDir`, `handler`, `include`, and `exclude` are ignored.
128
+
129
+ #### Performance
130
+
131
+ | Property | Type | Default | Description |
132
+ |---|---|---|---|
133
+ | `memorySize` | `number` | `1792` | Memory in MB. Also controls proportional CPU allocation. [Pricing](https://aws.amazon.com/lambda/pricing/) |
134
+ | `timeout` | `number` | `10` | Max execution time in seconds (max 900) |
135
+ | `tracing` | `boolean` | `false` | Enable [AWS X-Ray](https://aws.amazon.com/xray/) tracing |
136
+
137
+ #### Concurrency
138
+
139
+ | Property | Type | Description |
140
+ |---|---|---|
141
+ | `reservedConcurrency` | `number` | Hard cap on simultaneous executions. Prevents the function from consuming all account concurrency. |
142
+ | `provisionedConcurrency` | `number` | Pre-warmed instances. Eliminates cold starts for latency-sensitive workloads. Incurs additional cost. |
143
+
144
+ See [Lambda concurrency docs](https://docs.aws.amazon.com/lambda/latest/dg/configuration-concurrency.html).
145
+
146
+ #### Warm-up
147
+
148
+ | Property | Type | Default | Description |
149
+ |---|---|---|---|
150
+ | `keepWarm` | `boolean` | `false` | Creates an EventBridge rule that pings the function every 5 minutes to prevent cold starts |
151
+ | `url` | `boolean` | `false` | Enables a [Lambda Function URL](https://docs.aws.amazon.com/lambda/latest/dg/lambda-urls.html) (public, no auth) in addition to API Gateway |
152
+
153
+ #### Environment
154
+
155
+ | Property | Type | Description |
156
+ |---|---|---|
157
+ | `variables` | `Array<{ [key: string]: string }>` | Plain environment variables |
158
+ | `secrets` | `{ key: string; resource: string }[]` | Secrets Manager ARNs injected as env vars. Lambda is automatically granted read access. |
159
+
160
+ ### VPC
161
+
162
+ | Property | Type | Description |
163
+ |---|---|---|
164
+ | `vpc` | `IVpc \| IVpcLink` | Attach the Lambda to an existing VPC. Useful for accessing RDS, ElastiCache, or other private resources. |
165
+
166
+ ## API Gateway Details
167
+
168
+ - Uses [HTTP API](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api.html) (v2) - lower latency and cost than REST API
169
+ - Routes `GET` and `HEAD /{proxy+}` to the Lambda function
170
+ - No CORS preflight configured by default (add it in your handler if needed)
171
+ - Custom domain uses a regional endpoint with TLS 1.2
172
+
173
+ ## Stack Outputs
174
+
175
+ | Output | Description |
176
+ |---|---|
177
+ | `ApiGatewayUrl` | API Gateway endpoint |
178
+ | `LambdaFunction` | Lambda function name |
179
+ | `LambdaFunctionUrl` | Lambda Function URL (only if `url: true`) |
180
+ | `Route53Domain` | Custom domain URL (only if domain is configured) |
181
+
182
+ ## Related
183
+
184
+ - [lambda-basic.md](./lambda-basic.md) - Quick start
185
+ - [lambda-containers.md](./lambda-containers.md) - Container images and Bun runtime with Lambda