Skip to content

feat(event_handler): add support for form data in OpenAPI utility #7028

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
1b006c9
chore(openapi): add FileSchema & HeaderParamSchema helpers
oyiz-michael Jul 22, 2025
f52cb20
chore(openapi): leave FileSchema only (header already supported)
oyiz-michael Jul 23, 2025
5e1a550
feat(openapi): add multipart/form-data & form support
oyiz-michael Jul 23, 2025
5f2e22e
feat(openapi): add multipart/form-data & form support- documentation
oyiz-michael Jul 23, 2025
b413d4a
Merge branch 'develop' into feat/openapi-header-file-support
oyiz-michael Jul 23, 2025
0258158
run make format
oyiz-michael Jul 24, 2025
2ce5e20
Merge branch 'feat/openapi-header-file-support' of https://github.com…
oyiz-michael Jul 24, 2025
7f0cbd2
add comprehensive tests for File/Form OpenAPI support
oyiz-michael Jul 24, 2025
bdf2602
Merge branch 'develop' into feat/openapi-header-file-support
oyiz-michael Jul 24, 2025
4daaa28
make format
oyiz-michael Jul 24, 2025
5c4b1f0
additional test cases
oyiz-michael Jul 24, 2025
65d06ef
```
oyiz-michael Jul 24, 2025
18af9e6
defined a constant for the "name=" literal to avoid duplication in th…
oyiz-michael Jul 24, 2025
8c82bf9
make fmt
oyiz-michael Jul 24, 2025
9f0b738
sonar suggestion fix
oyiz-michael Jul 24, 2025
d1ef0fe
Added Comprehensive Test Coverage
oyiz-michael Jul 24, 2025
7ef0763
make format
oyiz-michael Jul 24, 2025
8013594
full test suite completed
oyiz-michael Jul 24, 2025
b5c0464
Refactoring and removing Form
leandrodamascena Jul 28, 2025
96372b1
Refactoring and removing Form
leandrodamascena Jul 28, 2025
6487a6a
Merge branch 'develop' into feat/openapi-header-file-support
leandrodamascena Jul 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions aws_lambda_powertools/event_handler/openapi/dependant.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@ def get_body_field(*, dependant: Dependant, name: str) -> ModelField | None:
alias="body",
field_info=body_field_info(**body_field_info_kwargs),
)

return final_field


Expand All @@ -367,11 +368,11 @@ def get_body_field_info(
body_field_info_kwargs["default"] = None

if any(isinstance(f.field_info, _File) for f in flat_dependant.body_params):
# MAINTENANCE: body_field_info: type[Body] = _File
raise NotImplementedError("_File fields are not supported in request bodies")
body_field_info = Body
body_field_info_kwargs["media_type"] = "multipart/form-data"
elif any(isinstance(f.field_info, _Form) for f in flat_dependant.body_params):
# MAINTENANCE: body_field_info: type[Body] = _Form
raise NotImplementedError("_Form fields are not supported in request bodies")
body_field_info = Body
body_field_info_kwargs["media_type"] = "application/x-www-form-urlencoded"
else:
body_field_info = Body

Expand Down
2 changes: 2 additions & 0 deletions aws_lambda_powertools/event_handler/openapi/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,8 @@ class OpenAPI(OpenAPIExtensions):
model_config = MODEL_CONFIG_ALLOW




model_rebuild(Schema)
model_rebuild(Operation)
model_rebuild(Encoding)
13 changes: 13 additions & 0 deletions aws_lambda_powertools/event_handler/openapi/params.py
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,13 @@ def __init__(
json_schema_extra: dict[str, Any] | None = None,
**extra: Any,
):
# For file uploads, ensure the OpenAPI schema has the correct format
file_schema_extra = {"format": "binary"}
if json_schema_extra:
json_schema_extra.update(file_schema_extra)
else:
json_schema_extra = file_schema_extra

super().__init__(
default=default,
default_factory=default_factory,
Expand Down Expand Up @@ -1122,3 +1129,9 @@ def _create_model_field(
required=field_info.default in (Required, Undefined),
field_info=field_info,
)


# Public type aliases for form and file parameters
# Use Annotated types to work properly with Pydantic
File = Annotated[bytes, _File()]
Form = Annotated[str, _Form()]
75 changes: 75 additions & 0 deletions docs/core/event_handler/api_gateway.md
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,81 @@ In the following example, we use a new `Header` OpenAPI type to add [one out of

1. `cloudfront_viewer_country` is a list that must contain values from the `CountriesAllowed` enumeration.

#### Handling file uploads and form data

!!! info "You must set `enable_validation=True` to handle file uploads and form data via type annotation."

We use the `Annotated` type to tell the Event Handler that a parameter expects file upload or form data. This automatically sets the correct OpenAPI schema for `multipart/form-data` requests.

In the following example, we use `File` and `Form` OpenAPI types to handle file uploads and form fields:

* `File` parameters expect binary file data and generate OpenAPI schema with `format: binary`
* `Form` parameters expect form field values from multipart form data
* The OpenAPI spec will automatically set `requestBody` content type to `multipart/form-data`

=== "handling_file_uploads.py"

```python hl_lines="5 9-10 18-19"
from typing import Annotated
from aws_lambda_powertools.event_handler import APIGatewayRestResolver
from aws_lambda_powertools.event_handler.openapi.params import File, Form

app = APIGatewayRestResolver(enable_validation=True)

@app.post("/upload")
def upload_file(
file: Annotated[bytes, File(description="File to upload")],
filename: Annotated[str, Form(description="Name of the file")]
):
# file contains the binary data of the uploaded file
# filename contains the form field value
return {
"message": f"Uploaded {filename}",
"size": len(file)
}

def lambda_handler(event, context):
return app.resolve(event, context)
```

1. If you're not using Python 3.9 or higher, you can install and use [`typing_extensions`](https://pypi.org/project/typing-extensions/){target="_blank" rel="nofollow"} to the same effect
2. `File` is a special OpenAPI type for binary file uploads that sets `format: binary` in the schema
3. `Form` is a special OpenAPI type for form field values in multipart requests

=== "Multiple files"

You can handle multiple file uploads by declaring parameters as lists:

```python hl_lines="9-10"
from typing import Annotated, List
from aws_lambda_powertools.event_handler import APIGatewayRestResolver
from aws_lambda_powertools.event_handler.openapi.params import File, Form

app = APIGatewayRestResolver(enable_validation=True)

@app.post("/upload-multiple")
def upload_multiple_files(
files: Annotated[List[bytes], File(description="Files to upload")],
description: Annotated[str, Form(description="Upload description")]
):
return {
"message": f"Uploaded {len(files)} files",
"description": description,
"total_size": sum(len(file) for file in files)
}
```

1. `files` will be a list containing the binary data of each uploaded file

???+ note "OpenAPI Schema Generation"
When you use `File` or `Form` parameters, the generated OpenAPI specification will automatically include:

* `requestBody` with content type `multipart/form-data`
* Proper schema definitions with `format: binary` for file parameters
* Form field descriptions and constraints

This ensures API documentation tools like SwaggerUI correctly display file upload interfaces.

#### Supported types for response serialization

With data validation enabled, we natively support serializing the following data types to JSON:
Expand Down
Loading