Description
Is this related to an existing feature request or issue?
Which Powertools for AWS Lambda (Python) utility does this relate to?
Event Handler - REST API
Summary
This RFC proposes the implementation of an automatic OpenAPI documentation generation feature for the AWS Lambda Powertools for Python's Event Handler - REST API utility.
The goal is to save developers time and effort by automating the generation of OpenAPI documentation based on the defined routes. The proposal suggests integrating this functionality into the existing utility, allowing developers to easily incorporate it into their applications.
Additionally, an optional Swagger UI endpoint would be exposed to provide a user-friendly interface for accessing the API documentation.
The scope of this proposal focuses solely on the auto-generation of OpenAPI docs from routes, excluding middleware support and authorization aspects. Some potential challenges discussed include handling dependencies, performance considerations, and deciding the level of exposure of the underlying apispec library.
Use case
The proposal aims to address the need for automatic generation of OpenAPI documentation based on the routes defined in the AWS Lambda Powertools for Python's Event Handler - REST API utility.
By automating the generation of OpenAPI docs, developers can save time and effort in maintaining the documentation manually. It ensures that the docs stay up to date with the application's routes, making the process efficient and hassle-free.
This automation optimizes developers' productivity and enhances the overall quality of the documentation, serving as a reliable reference for internal teams and external stakeholders.
With this integration of automation, developers can focus more on innovation and coding, knowing that their OpenAPI docs are effortlessly maintained and reflect the current state of the application.
Task list
- Merge PR feat(event_handler): generate OpenAPI specifications and validate input/output #3109
- Create documentation and examples
- Create a mechanism to expose a Swagger endpoint
- Create a CLI / utility to export an OpenAPI spec from an existing event handler API
- Create a more advanced CLI / utility to merge different APIs into a single Swagger specification
Proposal (updated 11/07/2023)
The design proposes integrating a functionality within the AWS Lambda Powertools for Python's Event Handler - REST API utility that auto-generates OpenAPI documentation based on the defined routes. The implementation should be intuitive and accessible for developers familiar with the project, allowing them to easily incorporate this feature into their applications.
We took inspiration from FastAPI and their excelent first class support for generationg OpenAPI specs. The implementation will need to introspect named parameters to add it to the documentation.
Additionally, we want to optionally expose a Swagger UI endpoint with the API documentation.
import json
from aws_lambda_powertools.event_handler import ApiGatewayResolver
from aws_lambda_powertools.event_handler.openapi import (
Example,
MediaType,
OpenAPIInfo,
Operation,
Parameter,
ParameterLocation,
Response,
Responses,
)
app = ApiGatewayResolver(generate_openapi=True, openapi_info=OpenAPIInfo(
title="This is my API",
version="0.0.1b",
))
@app.get(rule="/viewer",
operation=Operation(
operationId="geViewerDetailsv1",
summary="Viewer user information",
description="Returns information about the current signed-in user",
tags=["users"],
parameters=[
Parameter(name="include_details",
location=ParameterLocation.QUERY,
required=False,
schema={
"type": "boolean",
}),
],
responses=Responses(codes={
200: Response(description="Success",
content={
"application/json": MediaType(
schema={"$ref": "#/components/schemas/User"},
examples={
"application/json": Example(
value=json.dumps({"id": 1, "name": "John Smith"}),
),
},
),
}),
}),
))
def viewer(event: dict):
print(event)
print(app._spec.to_yaml())
Results in
paths:
/viewer:
get:
tags:
- users
summary: Viewer user information
description: Returns information about the current signed-in user
operationId: geViewerDetailsv1
parameters:
- name: include_details
in: query
required: false
schema:
type: boolean
responses:
'200':
description: Success
content:
application/json:
schema:
$ref: '#/components/schemas/User'
examples:
application/json:
value: '{"id": 1, "name": "John Smith"}'
summary: Viewer user information
info:
title: This is my API
version: 0.0.1b
openapi: 3.0.2
TODO:
- Explore way of defining global components (on the example, the User schema isn't defined in the spec)
- Make sure that the schema mechanism can be easily extended in the future (e.g: Pydantic models)
- Explore auto generation of parameters based on path parameters
- Create an example of serving the Swagger API
Enriching the OpenAPI data
OpenAPI is a rich spec that allows a lot of detail to be added. Currently, there's only a very limited amount of information we can derive automatically:
- HTTP method
- path
- parameters
- eventually CORS configuration
In order to allow the user to extend the specification with additional metadata, we can consider different options:
Expose the apispec
instance on the resolver's decorator (abandoned)
app = ApiGatewayResolver(generate_openapi=True)
app.spec.title = "Swagger Petstore"
app.spec.version = "1.0.0"
app.spec.path("/hello", operations={...})
@app.get("/hello")
def hello():
print("world")
Create a DTO for the OpenAPI properties (accepted)
app = ApiGatewayResolver(generate_openapi=True, openapi_properties={
"title": "Swagger Petstore",
"version": "1.0.0",
"openapi_version": "3.0.2"
})
@app.get("/hello", spec={
"operation": {
"summary": "This is a hello world endpoint",
...
}
})
In this case we could of course improve the DX by creating utility classes instead of making the user have to define arbitrary dictionaries.
Extend through documentation (abandoned for now)
This would follow the apispec-frameworks
model, where the documentation for the handler would get parsed and data would be extracted from it. Example from Flask:
class GistApi(MethodView):
'''Gist API.
---
x-extension: metadata
'''
def get(self):
'''Gist view
---
responses:
200:
schema:
$ref: '#/definitions/Gist'
'''
pass
def post(self):
pass
Serving a Swagger UI
We could add a new parameter to the resolver enabling a new GET /api-docs
route that would serve a Swagger UI.
app = ApiGatewayResolver(generate_openapi=True, enable_swagger=True, swagger_path="/api-docs")
This would still require configuring the mapping in the API Gateway (or equivalent).
To implement this, check how chalice-spec implements it, by loading the swagger ui from their CDN, and passing the API spec into the page as a JSON file.
Out of scope
The scope of this proposal does not include adding middleware support, such as incorporating authorization into the Swagger UI. While authorization is an important aspect of API documentation, it falls outside the scope of this specific RFC. The focus is solely on the auto-generation of OpenAPI docs from routes.
Additionally, we won't support any type of model validation for this RFC. This means we won't be able to attach Pydantic models to the API and have Powertools inspect and generate the appropriate OpenAPI sections for it.
Potential challenges
- Include apispec as a dependency or an optional dependency: the current version of apispec weighs around 29.6Kb. Should we just import the dependency, or mark it as optinal?
- Performance problems: should we opt-out from generating the API spec during a production workload? How to identify that we are running in production mode?
- How much of apispec to expose: should we just "escape-hatch" the apispec and let the customer use its API to enrich the API spec, or should we create a thin wrapper around it, allowing us to switch implementations in the future?
Acknowledgment
- This feature request meets Powertools for AWS Lambda (Python) Tenets
- Should this be considered in other Powertools for AWS Lambda languages? i.e. Java, TypeScript, and .NET
Metadata
Metadata
Assignees
Type
Projects
Status