Skip to content

Commit d8d4dbe

Browse files
dreamorosisvozza
authored andcommitted
docs: skeleton
1 parent 5b264fd commit d8d4dbe

11 files changed

+363
-35
lines changed

docs/features/event-handler/rest.md

Lines changed: 235 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,21 +31,246 @@ If you're using any API Gateway integration, you must have an existing [API Gate
3131

3232
In case of using [VPC Lattice](https://docs.aws.amazon.com/lambda/latest/dg/services-vpc-lattice.html){target="_blank"}, you must have a service network configured to invoke your Lambda function.
3333

34-
This is the sample infrastructure for API Gateway and Lambda Function URLs we are using for the examples in this documentation.
34+
This is the sample infrastructure for API Gateway and Lambda Function URLs we are using for the examples in this documentation. There is no additional permissions or dependencies required to use this utility.
3535

36-
???+ info "There is no additional permissions or dependencies required to use this utility."
36+
??? "See Infrastructure as Code (IaC) examples"
37+
=== "API Gateway SAM Template"
3738

38-
=== "API Gateway SAM Template"
39+
```yaml title="AWS Serverless Application Model (SAM) example"
40+
--8<-- "examples/snippets/event-handler/rest/templates/api_gateway.yml"
41+
```
3942

40-
```yaml title="AWS Serverless Application Model (SAM) example"
41-
--8<-- "examples/snippets/event-handler/rest/templates/api_gateway.yml"
43+
### Route events
44+
45+
Before you start defining your routes, it's important to understand how the event handler works with different types of events. The event handler can process events from API Gateway REST APIs, and will soon support ALB, Lambda Function URLs, and VPC Lattice as well.
46+
47+
When a request is received, the event handler will automatically convert the event into a `Request` object and give you access to the current request context, including headers, query parameters, and request body, as well as path parameters via typed arguments.
48+
49+
#### Response auto-serialization
50+
51+
!!! tip "Want full control over the response, headers, and status code? Read about the `Response` object here."
52+
53+
For your convenience, when you return a JavaScript object from your route handler, we automatically perform these actions:
54+
55+
* Auto-serialize the response to JSON and trim whitespace
56+
* Include the response under the appropriate equivalent of a `body`
57+
* Set the `Content-Type` header to `application/json`
58+
* Set the HTTP status code to 200 (OK)
59+
60+
=== "index.ts"
61+
62+
```ts hl_lines="6"
63+
--8<-- "examples/snippets/event-handler/rest/gettingStarted_serialization.ts"
4264
```
4365

44-
=== "Lambda Function URL SAM Template"
66+
1. This object will be serialized, trimmed, and included under the `body` key
67+
68+
=== "JSON response"
4569

46-
```yaml title="AWS Serverless Application Model (SAM) example"
47-
--8<-- "examples/snippets/event-handler/rest/templates/lambda_furl.yml"
70+
```json hl_lines="8"
71+
--8<-- "examples/snippets/event-handler/rest/samples/gettingStarted_serialization.json"
4872
```
4973

50-
<!-- remove line below while editing this doc & put it back until the doc has reached its first draft -->
51-
<!-- markdownlint-disable MD043 -->
74+
### Dynamic routes
75+
76+
You can use `/todos/:todoId` to configure dynamic URL paths, where `:todoId` will be resolved at runtime.
77+
78+
All dynamic route parameters will be available as typed object properties in the first argument of your route handler.
79+
80+
=== "index.ts"
81+
82+
```ts hl_lines="16"
83+
--8<-- "examples/snippets/event-handler/rest/gettingStarted_dynamic_routes.ts:3"
84+
```
85+
86+
=== "Request"
87+
88+
```json
89+
--8<-- "examples/snippets/event-handler/rest/samples/gettingStarted_dynamic_routes.json"
90+
```
91+
92+
You can also nest dynamic paths, for example `/todos/:todoId/comments/:commentId`, where both `:todoId` and `:commentId` will be resolved at runtime.
93+
94+
### HTTP Methods
95+
96+
You can use dedicated methods to specify the HTTP method that should be handled in each resolver. That is, `app.<httpMethod>`, where the HTTP method could be `get`, `post`, `put`, `patch`, and `delete`.
97+
98+
=== "index.ts"
99+
100+
```ts hl_lines="14 16"
101+
--8<-- "examples/snippets/event-handler/rest/gettingStarted_methods.ts:3"
102+
```
103+
104+
=== "Request"
105+
106+
```json
107+
--8<-- "examples/snippets/event-handler/rest/samples/gettingStarted_methods.json"
108+
```
109+
110+
If you need to accept multiple HTTP methods in a single function, or support a HTTP method for which no dedicated method exists (i.e. [`TRACE`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Methods/TRACE){target="_blank"}), you can use the `route` method and pass a list of HTTP methods.
111+
112+
=== "index.ts"
113+
114+
```ts hl_lines="21-24"
115+
--8<-- "examples/snippets/event-handler/rest/gettingStarted_multi_methods.ts:3"
116+
```
117+
118+
!!! tip
119+
We generally recommend to have separate functions for each HTTP method, as the functionality tends to differ depending on which method is used.
120+
121+
### Data validation
122+
123+
!!! note "Coming soon"
124+
Please open an issue if you would like us to prioritize this feature.
125+
126+
### Accessing request details
127+
128+
You can access request details such as headers, query parameters, and body using the `Request` object provided to your route handlers.
129+
130+
### Handling not found routes
131+
132+
!!! note "Coming soon"
133+
Please open an issue if you would like us to prioritize this feature.
134+
135+
### Error handling
136+
137+
!!! note "Coming soon"
138+
Please open an issue if you would like us to prioritize this feature.
139+
140+
### Raising HTTP errors
141+
142+
!!! note "Coming soon"
143+
Please open an issue if you would like us to prioritize this feature.
144+
145+
### Enabling SwaggerUI
146+
147+
!!! note "Coming soon"
148+
Please open an issue if you would like us to prioritize this feature.
149+
150+
### Custom domains
151+
152+
!!! note "Coming soon"
153+
Please open an issue if you would like us to prioritize this feature.
154+
155+
## Advanced
156+
157+
### CORS
158+
159+
You can configure CORS at the router level via the `cors` middleware.
160+
161+
!!! note "Coming soon"
162+
163+
### Middleware
164+
165+
// TODO: @svozza
166+
167+
### Fine grained responses
168+
169+
You can use the Web API's `Response` object to have full control over the response. For example, you might want to add additional headers, cookies, or set a custom content type.
170+
171+
// TODO: @svozza please add a code example + response sample
172+
173+
### Response streaming
174+
175+
!!! note "Coming soon"
176+
Please open an issue if you would like us to prioritize this feature.
177+
178+
### Compress
179+
180+
You can compress with gzip and base64 encode your responses via the `compress` parameter. You have the option to pass the `compress` parameter when working with a specific route or setting the correct `Accept-Encoding` header in the `Response` object.
181+
182+
!!! note "Coming soon"
183+
Please open an issue if you would like us to prioritize this feature.
184+
185+
### Binary responses
186+
187+
!!! warning "Using API Gateway?"
188+
Amazon API Gateway does not support `*/*` binary media type when [CORS](#cors) is also configured. This feature requires API Gateway to configure binary media types, see our [sample infrastructure](#required-resources) for reference.
189+
190+
For convenience, we automatically base64 encode binary responses. You can also use it in combination with the `compress` parameter if your client supports gzip.
191+
192+
Like the `compress` feature, the client must send the `Accept` header with the correct media type.
193+
194+
!!! tip Lambda Function URLs handle binary media types automatically.
195+
196+
!!! note "Coming soon"
197+
Please open an issue if you would like us to prioritize this feature.
198+
199+
### Debug mode
200+
201+
You can enable debug mode via the `POWERTOOLS_DEV` environment variable.
202+
203+
This will enable full stack traces errors in the response, log request and responses, and set CORS in development mode.
204+
205+
!!! note "Coming soon"
206+
Please open an issue if you would like us to prioritize this feature.
207+
208+
### OpenAPI
209+
210+
When you enable [Data Validation](#data-validation), we use a combination of Zod and JSON Schemas to add constraints to your API's parameters.
211+
212+
In OpenAPI documentation tools like [SwaggerUI](#enabling-swaggerui), these annotations become readable descriptions, offering a self-explanatory API interface. This reduces boilerplate code while improving functionality and enabling auto-documentation.
213+
214+
!!! note "Coming soon"
215+
Please open an issue if you would like us to prioritize this feature.
216+
217+
### Split routers
218+
219+
As you grow the number of routes a given Lambda function should handle, it is natural to either break into smaller Lambda functions, or split routes into separate files to ease maintenance - that's where the split `Router` feature is useful.
220+
221+
!!! note "Coming soon"
222+
Please open an issue if you would like us to prioritize this feature.
223+
224+
### Considerations
225+
226+
This utility is optimized for AWS Lambda computing model and prioritizes fast startup, minimal feature set, and quick onboarding for triggers supported by Lambda.
227+
228+
Event Handler naturally leads to a single Lambda function handling multiple routes for a given service, which can be eventually broken into multiple functions.
229+
230+
Both single (monolithic) and multiple functions (micro) offer different set of trade-offs worth knowing.
231+
232+
!!! tip "TL;DR;"
233+
Start with a monolithic function, add additional functions with new handlers, and possibly break into micro functions if necessary.
234+
235+
#### Monolithic function
236+
237+
![monolithic function](../../media//monolithic-function.png)
238+
239+
A monolithic function means that your final code artifact will be deployed to a single function. This is generally the best approach to start.
240+
241+
_**Benefits**_
242+
243+
* **Code reuse.** It's easier to reason about your service, modularize it and reuse code as it grows. Eventually, it can be turned into a standalone library.
244+
* **No custom tooling.** Monolithic functions are treated just like normal Python packages; no upfront investment in tooling.
245+
* **Faster deployment and debugging.** Whether you use all-at-once, linear, or canary deployments, a monolithic function is a single deployable unit. IDEs like PyCharm and VSCode have tooling to quickly profile, visualize, and step through debug any Python package.
246+
247+
_**Downsides**_
248+
249+
* **Cold starts.** Frequent deployments and/or high load can diminish the benefit of monolithic functions depending on your latency requirements, due to [Lambda scaling model](https://docs.aws.amazon.com/lambda/latest/dg/invocation-scaling.html){target="_blank"}. Always load test to pragmatically balance between your customer experience and development cognitive load.
250+
* **Granular security permissions.** The micro function approach enables you to use fine-grained permissions & access controls, separate external dependencies & code signing at the function level. Conversely, you could have multiple functions while duplicating the final code artifact in a monolithic approach. Regardless, least privilege can be applied to either approaches.
251+
* **Higher risk per deployment.** A misconfiguration or invalid import can cause disruption if not caught earlier in automated testing. Multiple functions can mitigate misconfigurations but they would still share the same code artifact. You can further minimize risks with multiple environments in your CI/CD pipeline.
252+
253+
#### Micro function
254+
255+
![micro function](../../media//micro-function.png)
256+
257+
A micro function means that your final code artifact will be different to each function deployed. This is generally the approach to start if you're looking for fine-grain control and/or high load on certain parts of your service.
258+
259+
_**Benefits**_
260+
261+
**Granular scaling.** A micro function can benefit from the [Lambda scaling model](https://docs.aws.amazon.com/lambda/latest/dg/invocation-scaling.html){target="_blank"} to scale differently depending on each part of your application. Concurrency controls and provisioned concurrency can also be used at a granular level for capacity management.
262+
**Discoverability.** Micro functions are easier to visualize when using distributed tracing. Their high-level architectures can be self-explanatory, and complexity is highly visible — assuming each function is named to the business purpose it serves.
263+
**Package size.** An independent function can be significant smaller (KB vs MB) depending on external dependencies it require to perform its purpose. Conversely, a monolithic approach can benefit from [Lambda Layers](https://docs.aws.amazon.com/lambda/latest/dg/invocation-layers.html){target="_blank"} to optimize builds for external dependencies.
264+
265+
_**Downsides**_
266+
267+
**Upfront investment.** You need custom build tooling to bundle assets, including [native bindings for runtime compatibility](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html){target="_blank"}. Operations become more elaborate — you need to standardize tracing labels/annotations, structured logging, and metrics to pinpoint root causes.
268+
**Engineering discipline** is necessary for both approaches. Micro-function approach however requires further attention in consistency as the number of functions grow, just like any distributed system.
269+
**Harder to share code.** Shared code must be carefully evaluated to avoid unnecessary deployments when that changes. Equally, if shared code isn't a library, your development, building, deployment tooling need to accommodate the distinct layout.
270+
**Slower safe deployments.** Safely deploying multiple functions require coordination — AWS CodeDeploy deploys and verifies each function sequentially. This increases lead time substantially (minutes to hours) depending on the deployment strategy you choose. You can mitigate it by selectively enabling it in prod-like environments only, and where the risk profile is applicable.
271+
Automated testing, operational and security reviews are essential to stability in either approaches.
272+
273+
## Testing your code
274+
275+
!!! note "Coming soon"
276+
Please open an issue if you would like us to prioritize this section.

docs/media/micro-function.png

84.8 KB
Loading

docs/media/monolithic-function.png

79.8 KB
Loading
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
declare function getTodoById<T>(todoId: unknown): Promise<{ id: string } & T>;
2+
3+
import { Router } from '@aws-lambda-powertools/event-handler/experimental-rest';
4+
import { Logger } from '@aws-lambda-powertools/logger';
5+
import {
6+
correlationPaths,
7+
search,
8+
} from '@aws-lambda-powertools/logger/correlationId';
9+
import type { Context } from 'aws-lambda/handler';
10+
11+
const logger = new Logger({
12+
correlationIdSearchFn: search,
13+
});
14+
const app = new Router({ logger });
15+
16+
app.get('/todos/:todoId', async ({ todoId }) => {
17+
const todo = await getTodoById(todoId);
18+
return { todo };
19+
});
20+
21+
export const handler = async (event: unknown, context: Context) => {
22+
// You can continue using other utilities just as before
23+
logger.addContext(context);
24+
logger.setCorrelationId(event, correlationPaths.API_GATEWAY_REST);
25+
return app.resolve(event, context);
26+
};
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
declare function putTodo<T>(todo: unknown): Promise<{ id: string } & T>;
2+
3+
import { Router } from '@aws-lambda-powertools/event-handler/experimental-rest';
4+
import { Logger } from '@aws-lambda-powertools/logger';
5+
import {
6+
correlationPaths,
7+
search,
8+
} from '@aws-lambda-powertools/logger/correlationId';
9+
import type { Context } from 'aws-lambda/handler';
10+
11+
const logger = new Logger({
12+
correlationIdSearchFn: search,
13+
});
14+
const app = new Router({ logger });
15+
16+
app.post('/todos', async (_, { request }) => {
17+
const body = await request.json();
18+
const todo = await putTodo(body);
19+
20+
return todo;
21+
});
22+
23+
export const handler = async (event: unknown, context: Context) => {
24+
// You can continue using other utilities just as before
25+
logger.addContext(context);
26+
logger.setCorrelationId(event, correlationPaths.API_GATEWAY_REST);
27+
return app.resolve(event, context);
28+
};
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
declare function putTodo<T>(todo: unknown): Promise<{ id: string } & T>;
2+
3+
import { Router } from '@aws-lambda-powertools/event-handler/experimental-rest';
4+
import { Logger } from '@aws-lambda-powertools/logger';
5+
import {
6+
correlationPaths,
7+
search,
8+
} from '@aws-lambda-powertools/logger/correlationId';
9+
import type { Context } from 'aws-lambda/handler';
10+
11+
const logger = new Logger({
12+
correlationIdSearchFn: search,
13+
});
14+
const app = new Router({ logger });
15+
16+
app.route(
17+
async (_, { request }) => {
18+
const body = await request.json();
19+
const todo = await putTodo(body);
20+
21+
return todo;
22+
},
23+
{
24+
path: '/todos',
25+
method: ['POST', 'PUT'],
26+
}
27+
);
28+
29+
export const handler = async (event: unknown, context: Context) => {
30+
// You can continue using other utilities just as before
31+
logger.addContext(context);
32+
logger.setCorrelationId(event, correlationPaths.API_GATEWAY_REST);
33+
return app.resolve(event, context);
34+
};
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { Router } from '@aws-lambda-powertools/event-handler/experimental-rest';
2+
3+
const app = new Router();
4+
5+
app.get('/ping', async () => {
6+
return { message: 'pong' }; // (1)!
7+
});
8+
9+
export const handler = app.resolve;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"resource": "/todos/{id}",
3+
"path": "/todos/1",
4+
"httpMethod": "GET"
5+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"resource": "/todos",
3+
"path": "/todos",
4+
"httpMethod": "POST",
5+
"body": "{\"title\": \"foo\", \"userId\": 1, \"completed\": false}"
6+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"statusCode": 200,
3+
"multiValueHeaders": {
4+
"Content-Type": [
5+
"application/json"
6+
]
7+
},
8+
"body": "{'message':'pong'}",
9+
"isBase64Encoded": false
10+
}

0 commit comments

Comments
 (0)