Skip to content

Commit d017744

Browse files
docs(event_handler): add section about Pydantic serialization (#7049)
* docs(event_handler): add section about Pydantic serialization * remove http section
1 parent 5999346 commit d017744

File tree

4 files changed

+89
-0
lines changed

4 files changed

+89
-0
lines changed

docs/core/event_handler/api_gateway.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,38 @@ With data validation enabled, we natively support serializing the following data
554554
???+ info "See [custom serializer section](#custom-serializer) for bringing your own."
555555
Otherwise, we will raise `SerializationError` for any unsupported types _e.g., SQLAlchemy models_.
556556

557+
#### Pydantic serialization behavior
558+
559+
!!! warning "Important: Pydantic models are serialized using aliases by default"
560+
When `enable_validation=True` is set, **all Pydantic models in responses are automatically serialized using `by_alias=True`**. This differs from Pydantic's default behavior (`by_alias=False`).
561+
562+
When you return Pydantic models from your handlers, Event Handler will serialize them using field aliases defined in your model configuration:
563+
564+
=== "pydantic_alias_serialization.py"
565+
566+
```python hl_lines="1 2 11 20"
567+
--8<-- "examples/event_handler_rest/src/pydantic_alias_serialization.py"
568+
```
569+
570+
1. The `alias_generator=to_snake` converts camelCase field names to snake_case aliases
571+
2. `firstName` becomes `first_name` and lastName` becomes `last_name` in the JSON response
572+
573+
=== "pydantic_alias_serialization_output.json"
574+
575+
```json hl_lines="3"
576+
--8<-- "examples/event_handler_rest/src/pydantic_alias_serialization_output.json"
577+
```
578+
579+
##### Serializing using field name
580+
581+
If you need to serialize using field names instead of aliases, you can dump the model:
582+
583+
=== "pydantic_field_name_serialization.py"
584+
585+
```python hl_lines="11 19 20"
586+
--8<-- "examples/event_handler_rest/src/pydantic_field_name_serialization.py"
587+
```
588+
557589
### Accessing request details
558590

559591
Event Handler integrates with [Event Source Data Classes utilities](../../utilities/data_classes.md){target="_blank"}, and it exposes their respective resolver request details and convenient methods under `app.current_event`.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from pydantic import BaseModel, ConfigDict
2+
from pydantic.alias_generators import to_snake
3+
4+
from aws_lambda_powertools.event_handler import APIGatewayRestResolver
5+
from aws_lambda_powertools.utilities.typing import LambdaContext
6+
7+
app = APIGatewayRestResolver(enable_validation=True)
8+
9+
10+
class UserResponse(BaseModel):
11+
model_config = ConfigDict(alias_generator=to_snake, populate_by_name=True) # (1)!
12+
13+
firstName: str # Will be serialized as "first_name"
14+
lastName: str # Will be serialized as "last_name"
15+
16+
17+
@app.get("/user")
18+
def get_user() -> UserResponse:
19+
return UserResponse(firstName="John", lastName="Doe") # (2)!
20+
# Response will be: {"first_name": "John", "last_name": "Doe"}
21+
22+
23+
def lambda_handler(event: dict, context: LambdaContext) -> dict:
24+
return app.resolve(event, context)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"statusCode": 200,
3+
"body": "{\"first_name\":\"John\",\"last_name\":\"Doe\"}",
4+
"headers": {
5+
"Content-Type": "application/json"
6+
},
7+
"multiValueHeaders": {},
8+
"isBase64Encoded": false
9+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from pydantic import BaseModel, ConfigDict
2+
from pydantic.alias_generators import to_snake
3+
4+
from aws_lambda_powertools.event_handler import APIGatewayRestResolver
5+
from aws_lambda_powertools.utilities.typing import LambdaContext
6+
7+
app = APIGatewayRestResolver(enable_validation=True)
8+
9+
10+
class UserResponse(BaseModel):
11+
model_config = ConfigDict(alias_generator=to_snake, populate_by_name=True)
12+
13+
firstName: str
14+
lastName: str
15+
16+
17+
@app.get("/user")
18+
def get_user_manual() -> dict:
19+
user = UserResponse(firstName="John", lastName="Doe")
20+
return user.model_dump(by_alias=False) # Returns dict, not UserResponse
21+
22+
23+
def lambda_handler(event: dict, context: LambdaContext) -> dict:
24+
return app.resolve(event, context)

0 commit comments

Comments
 (0)