effortless-aws 0.4.1 → 0.5.0
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.
- package/README.md +15 -234
- package/dist/cli/index.js +2008 -1020
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +65 -17
- package/dist/index.js +18 -9
- package/dist/index.js.map +1 -1
- package/dist/runtime/{wrap-site.js → wrap-app.js} +18 -27
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
# effortless-aws
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/effortless-aws)
|
|
4
|
+
[](https://www.npmjs.com/package/effortless-aws)
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
**Effortless** is a TypeScript framework for developers who build on AWS serverless. It handles three things:
|
|
8
|
-
|
|
9
|
-
1. **Infrastructure from code.** You write handlers, export them, and deploy. The framework derives Lambda functions, API Gateway routes, DynamoDB tables, streams, and IAM roles directly from your TypeScript exports. No config files.
|
|
10
|
-
2. **Bundling and packaging.** Your code is automatically bundled with [esbuild](https://esbuild.github.io/) — tree-shaken, split per function, with shared dependencies extracted into a common [Lambda Layer](https://docs.aws.amazon.com/lambda/latest/dg/chapter-layers.html). No build config to maintain.
|
|
11
|
-
3. **Typed cross-resource communication.** Reference one handler from another with `deps: { orders }` and get a fully typed client injected at runtime, with IAM permissions wired automatically. Serverless resources talk to each other through code, not through copied ARNs and manual policies.
|
|
6
|
+
TypeScript framework for AWS serverless. Export handlers, deploy with one command. No YAML, no CloudFormation, no state files.
|
|
12
7
|
|
|
13
8
|
```bash
|
|
14
9
|
npm install effortless-aws
|
|
@@ -32,236 +27,22 @@ export const hello = defineHttp({
|
|
|
32
27
|
npx eff deploy
|
|
33
28
|
```
|
|
34
29
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
## Why
|
|
38
|
-
|
|
39
|
-
Traditional Lambda development splits infrastructure and code across multiple files and languages. Adding a single endpoint means touching CloudFormation/CDK/Terraform templates, IAM policies, and handler code separately.
|
|
40
|
-
|
|
41
|
-
**Effortless** derives infrastructure from your TypeScript exports. One `defineHttp` call creates the API Gateway route, Lambda function, and IAM role. One `defineTable` call creates the DynamoDB table, stream, event source mapping, and processor Lambda.
|
|
42
|
-
|
|
43
|
-
## Killer features
|
|
44
|
-
|
|
45
|
-
**Infrastructure from code** — export a handler, get the AWS resources. No config files, no YAML.
|
|
46
|
-
|
|
47
|
-
**Typed everything** — `defineTable` schema gives you typed `table.put()`, typed `deps.orders.get()`, typed `record.new`. One definition, types flow everywhere.
|
|
48
|
-
|
|
49
|
-
**Direct AWS SDK deploys** — no CloudFormation, no Pulumi. Direct API calls. Deploy in ~5-10s, not 5-10 minutes.
|
|
50
|
-
|
|
51
|
-
**No state files** — AWS resource tags are the source of truth. No tfstate, no S3 backends, no drift.
|
|
52
|
-
|
|
53
|
-
**Cross-handler deps** — `deps: { orders }` auto-wires IAM permissions and injects a typed `TableClient`. Zero config.
|
|
54
|
-
|
|
55
|
-
**SSM params** — `param("stripe-key")` fetches from Parameter Store at cold start. Auto IAM, auto caching, supports transforms.
|
|
56
|
-
|
|
57
|
-
**Partial batch failures** — DynamoDB stream processing reports failed records individually. No batch-level retries for one bad record.
|
|
58
|
-
|
|
59
|
-
**Cold start caching** — `context` factory runs once per cold start, cached across invocations. Put DB connections, SDK clients, config there.
|
|
60
|
-
|
|
61
|
-
## Examples
|
|
62
|
-
|
|
63
|
-
### Path params
|
|
64
|
-
|
|
65
|
-
```typescript
|
|
66
|
-
export const getUser = defineHttp({
|
|
67
|
-
method: "GET",
|
|
68
|
-
path: "/users/{id}",
|
|
69
|
-
onRequest: async ({ req }) => {
|
|
70
|
-
const user = await findUser(req.params.id);
|
|
71
|
-
return { status: 200, body: user };
|
|
72
|
-
},
|
|
73
|
-
});
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
> Creates: Lambda function, API Gateway `GET /users/{id}` route, IAM execution role.
|
|
77
|
-
|
|
78
|
-
### Schema validation
|
|
79
|
-
|
|
80
|
-
Works with any validation library — Zod, Effect Schema, or a plain function.
|
|
81
|
-
|
|
82
|
-
```typescript
|
|
83
|
-
export const createUser = defineHttp({
|
|
84
|
-
method: "POST",
|
|
85
|
-
path: "/users",
|
|
86
|
-
schema: (input) => parseUser(input),
|
|
87
|
-
onRequest: async ({ data }) => {
|
|
88
|
-
// data is typed from schema return type
|
|
89
|
-
return { status: 201, body: { id: data.id } };
|
|
90
|
-
},
|
|
91
|
-
});
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
### Context (cold-start cache)
|
|
95
|
-
|
|
96
|
-
`context` runs once per cold start. Put SDK clients, DB connections, config here.
|
|
97
|
-
|
|
98
|
-
```typescript
|
|
99
|
-
export const listOrders = defineHttp({
|
|
100
|
-
method: "GET",
|
|
101
|
-
path: "/orders",
|
|
102
|
-
context: () => ({
|
|
103
|
-
db: new DatabaseClient(),
|
|
104
|
-
}),
|
|
105
|
-
onRequest: async ({ ctx }) => {
|
|
106
|
-
const orders = await ctx.db.findAll();
|
|
107
|
-
return { status: 200, body: orders };
|
|
108
|
-
},
|
|
109
|
-
});
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
### SSM params
|
|
113
|
-
|
|
114
|
-
`param("key")` reads from Parameter Store at cold start. Auto IAM, auto caching.
|
|
115
|
-
|
|
116
|
-
```typescript
|
|
117
|
-
export const charge = defineHttp({
|
|
118
|
-
method: "POST",
|
|
119
|
-
path: "/charge",
|
|
120
|
-
params: { apiKey: param("stripe-key") },
|
|
121
|
-
context: async ({ params }) => ({
|
|
122
|
-
stripe: new Stripe(params.apiKey),
|
|
123
|
-
}),
|
|
124
|
-
onRequest: async ({ ctx, data }) => {
|
|
125
|
-
await ctx.stripe.charges.create(data);
|
|
126
|
-
return { status: 200, body: { ok: true } };
|
|
127
|
-
},
|
|
128
|
-
});
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
> Creates: Lambda, API Gateway route, IAM role with `ssm:GetParameter` on `/{project}/{stage}/stripe-key`.
|
|
132
|
-
|
|
133
|
-
### Cross-handler deps
|
|
134
|
-
|
|
135
|
-
`deps: { orders }` auto-wires IAM and injects a typed `TableClient`.
|
|
136
|
-
|
|
137
|
-
```typescript
|
|
138
|
-
type Order = { id: string; name: string };
|
|
139
|
-
|
|
140
|
-
export const orders = defineTable<Order>({
|
|
141
|
-
pk: { name: "id", type: "string" },
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
export const createOrder = defineHttp({
|
|
145
|
-
method: "POST",
|
|
146
|
-
path: "/orders",
|
|
147
|
-
deps: { orders },
|
|
148
|
-
onRequest: async ({ deps }) => {
|
|
149
|
-
await deps.orders.put({ id: crypto.randomUUID(), name: "New order" });
|
|
150
|
-
return { status: 201, body: { ok: true } };
|
|
151
|
-
},
|
|
152
|
-
});
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
> Creates: DynamoDB table, Lambda, API Gateway route. The Lambda's IAM role gets DynamoDB read/write permissions on the `orders` table automatically. Table name is injected via environment variable.
|
|
156
|
-
|
|
157
|
-
### DynamoDB table (resource only)
|
|
158
|
-
|
|
159
|
-
Export a table — get the DynamoDB resource. No Lambda, no stream.
|
|
160
|
-
|
|
161
|
-
```typescript
|
|
162
|
-
export const sessions = defineTable({
|
|
163
|
-
pk: { name: "id", type: "string" },
|
|
164
|
-
ttlAttribute: "expiresAt",
|
|
165
|
-
});
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
> Creates: DynamoDB table with TTL enabled. No Lambda, no stream — just the table.
|
|
169
|
-
|
|
170
|
-
### DynamoDB table with stream
|
|
171
|
-
|
|
172
|
-
Add `onRecord` to process changes. Each record is handled individually with automatic partial batch failure reporting.
|
|
173
|
-
|
|
174
|
-
```typescript
|
|
175
|
-
type User = { id: string; email: string; name: string };
|
|
176
|
-
|
|
177
|
-
export const users = defineTable<User>({
|
|
178
|
-
pk: { name: "id", type: "string" },
|
|
179
|
-
sk: { name: "email", type: "string" },
|
|
180
|
-
onRecord: async ({ record }) => {
|
|
181
|
-
if (record.eventName === "INSERT") {
|
|
182
|
-
await sendWelcomeEmail(record.new!.email);
|
|
183
|
-
}
|
|
184
|
-
},
|
|
185
|
-
});
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
> Creates: DynamoDB table with stream enabled, Lambda for stream processing, event source mapping between them, IAM role with DynamoDB read/write permissions. Failed records are reported individually via [partial batch responses](https://docs.aws.amazon.com/lambda/latest/dg/with-ddb.html#services-ddb-batchfailurereporting).
|
|
189
|
-
|
|
190
|
-
### Full example
|
|
191
|
-
|
|
192
|
-
Everything together — table, HTTP handler with validation, deps, params, and context.
|
|
193
|
-
|
|
194
|
-
```typescript
|
|
195
|
-
type Order = { id: string; total: number; chargeId?: string };
|
|
196
|
-
|
|
197
|
-
export const orders = defineTable<Order>({
|
|
198
|
-
pk: { name: "id", type: "string" },
|
|
199
|
-
onRecord: async ({ record }) => {
|
|
200
|
-
if (record.eventName === "INSERT") {
|
|
201
|
-
await notifyWarehouse(record.new!);
|
|
202
|
-
}
|
|
203
|
-
},
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
export const createOrder = defineHttp({
|
|
207
|
-
method: "POST",
|
|
208
|
-
path: "/orders",
|
|
209
|
-
schema: (input) => parseOrder(input),
|
|
210
|
-
deps: { orders },
|
|
211
|
-
params: { apiKey: param("stripe-key") },
|
|
212
|
-
context: async ({ params }) => ({
|
|
213
|
-
stripe: new Stripe(params.apiKey),
|
|
214
|
-
}),
|
|
215
|
-
onRequest: async ({ data, ctx, deps }) => {
|
|
216
|
-
const charge = await ctx.stripe.charges.create({ amount: data.total });
|
|
217
|
-
await deps.orders.put({ id: crypto.randomUUID(), ...data, chargeId: charge.id });
|
|
218
|
-
return { status: 201, body: { ok: true } };
|
|
219
|
-
},
|
|
220
|
-
});
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
## Configuration
|
|
224
|
-
|
|
225
|
-
```typescript
|
|
226
|
-
// effortless.config.ts
|
|
227
|
-
import { defineConfig } from "effortless-aws";
|
|
228
|
-
|
|
229
|
-
export default defineConfig({
|
|
230
|
-
name: "my-app",
|
|
231
|
-
region: "eu-central-1",
|
|
232
|
-
handlers: ["src/**/*.ts"],
|
|
233
|
-
});
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
## CLI
|
|
237
|
-
|
|
238
|
-
```bash
|
|
239
|
-
npx eff deploy # deploy all handlers
|
|
240
|
-
npx eff deploy --stage prod # deploy to specific stage
|
|
241
|
-
npx eff deploy --only users # deploy single handler
|
|
242
|
-
npx eff destroy # remove all resources
|
|
243
|
-
npx eff logs users --follow # stream CloudWatch logs
|
|
244
|
-
npx eff list # show deployed resources
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
## How it works
|
|
30
|
+
One export, one command. Lambda, API Gateway route, and IAM role created automatically.
|
|
248
31
|
|
|
249
|
-
|
|
250
|
-
2. **Bundle** (esbuild) — wraps each handler with a runtime adapter
|
|
251
|
-
3. **Deploy** (AWS SDK) — creates/updates Lambda, API Gateway, DynamoDB, IAM directly
|
|
32
|
+
## Features
|
|
252
33
|
|
|
253
|
-
|
|
34
|
+
- **Infrastructure from code** — export a handler, get the AWS resources. No config files.
|
|
35
|
+
- **Typed everything** — `defineTable<Order>` gives you typed `put()`, typed `deps.orders.get()`, typed `record.new`.
|
|
36
|
+
- **Direct AWS SDK deploys** — no CloudFormation. Deploy in ~5-10s, not minutes.
|
|
37
|
+
- **No state files** — AWS resource tags are the source of truth.
|
|
38
|
+
- **Cross-handler deps** — `deps: { orders }` auto-wires IAM and injects a typed `TableClient`.
|
|
39
|
+
- **SSM params** — `param("stripe-key")` fetches from Parameter Store at cold start. Auto IAM, auto caching.
|
|
40
|
+
- **Partial batch failures** — DynamoDB stream processing reports failed records individually.
|
|
41
|
+
- **Cold start caching** — `context` factory runs once per cold start, cached across invocations.
|
|
254
42
|
|
|
255
|
-
##
|
|
43
|
+
## Documentation
|
|
256
44
|
|
|
257
|
-
|
|
258
|
-
|---|---|---|---|---|
|
|
259
|
-
| Infra from code (not config) | No | Yes | No | **Yes** |
|
|
260
|
-
| Typed client from schema | No | No | No | **Yes** |
|
|
261
|
-
| No state files | No | No | No | **Yes** |
|
|
262
|
-
| Deploy speed | ~30s | ~30s | minutes | **~5-10s** |
|
|
263
|
-
| Runs in your AWS account | Yes | Yes | Yes | **Yes** |
|
|
264
|
-
| Open source | Yes | Yes | Yes | **Yes** |
|
|
45
|
+
Full docs, examples, and API reference: **[effortless-aws docs](https://effortless-aws.website)**
|
|
265
46
|
|
|
266
47
|
## License
|
|
267
48
|
|