What's good about generating the schema classes used by FastAPI from an OpenAPI definition?
- You can verify the design using only the OpenAPI definition
- No need to write the classes in FastAPI by hand
Preparing OpenAPI
We prepare the FastAPI source code and the OpenAPI definition with a layout like this. By the way, the definition file's path is generated/openapi.json because we use openapi-generator to generate openapi.json, and this is a common setup at our company.
project
L api
L main.py : FastAPI's main.py
L schema
L generated
L openapi.json : OpenAPI definition file
Installing datamodel-code-generator
To generate schema classes from the OpenAPI definition, we use datamodel-code-generator. Run the following command to install it into your python environment.
# Install command
$ pip install datamodel-code-generator
$ datamodel-codegen --version
0.11.19
Outputting schemas.py
Next, run the following command to output the schema file into the FastAPI project.
datamodel-codegen --input /schema/generated/openapi.json --input-file-type openapi --output api/schemas.py
As an example, running this against the following OpenAPI definition produced python source code like this.
schema/generated/openapi.json
{
"openapi" : "3.0.3",
"info" : {
"title" : "api",
"version" : "0.0.1"
},
"servers" : [ {
"url" : "/"
} ],
"tags" : [ {
"description" : "index",
"name" : "index"
} ],
"paths" : {
"/" : {
"post" : {
"description" : "POST",
"operationId" : "IndexPost",
"requestBody" : {
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/IndexPostRequest"
}
}
},
"description" : "POST",
"required" : true
},
"responses" : {
"200" : {
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/IndexPostResponse"
}
}
},
"description" : "OK"
}
},
"tags" : [ "index" ]
}
}
},
"components" : {
"schemas" : {
"IndexPostRequest" : {
"properties" : {
"name" : {
"type" : "string"
}
},
"type" : "object"
},
"IndexPostResponse" : {
"properties" : {
"hello" : {
"type" : "string"
}
},
"type" : "object"
}
}
}
}
api/schemas.py
# generated by datamodel-codegen:
# filename: openapi.json
# timestamp: 2022-02-25T07:02:11+00:00
from __future__ import annotations
from typing import Optional
from pydantic import BaseModel
class IndexPostRequest(BaseModel):
name: Optional[str] = None
class IndexPostResponse(BaseModel):
hello: Optional[str] = None
Implementing in FastAPI
Let's implement the generated schemas.py in FastAPI. We changed api/main.py as follows.
api/main.py
from FastAPI import FastAPI
from schemas import IndexPostRequest, IndexPostResponse
app = FastAPI()
@app.post("/", response_model=IndexPostResponse)
def read_root(request:IndexPostRequest) -> IndexPostResponse:
response = IndexPostResponse()
response.hello = request.name
return response
Verifying it works
We ran FastAPI locally and executed the following command to check that it behaves as expected.
$ curl -X 'POST' \
'http://0.0.0.0:8000/' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"name": "World"
}'
{"hello":"World"}
We got the expected value back.
Summary
OpenAPI has such a rich set of class generation tools available, which is really great.