# openapi-pydantic [![PyPI](https://img.shields.io/pypi/v/openapi-pydantic)](https://pypi.org/project/openapi-pydantic/) [![PyPI - License](https://img.shields.io/pypi/l/openapi-pydantic)](https://github.com/mike-oakley/openapi-pydantic/blob/main/LICENSE) OpenAPI schema implemented in [Pydantic](https://github.com/samuelcolvin/pydantic). Both Pydantic 1.8+ and 2.x are supported. The naming of the classes follows the schema in [OpenAPI specification](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.1.md#schema). > This library is forked from [OpenAPI Schema Pydantic](https://github.com/kuimono/openapi-schema-pydantic) (at version [1.2.4](https://github.com/kuimono/openapi-schema-pydantic/releases/tag/v1.2.4)) which is no longer actively maintained. ## Installation `pip install openapi-pydantic` ## Try me ```python from openapi_pydantic import OpenAPI, Info, PathItem, Operation, Response # Construct OpenAPI by pydantic objects open_api = OpenAPI( info=Info( title="My own API", version="v0.0.1", ), paths={ "/ping": PathItem( get=Operation( responses={ "200": Response( description="pong" ) } ) ) }, ) # Note: for Pydantic 1.x, replace `model_dump_json` with `json` print(open_api.model_dump_json(by_alias=True, exclude_none=True, indent=2)) ``` Result: ```json { "openapi": "3.1.1", "info": { "title": "My own API", "version": "v0.0.1" }, "servers": [ { "url": "/" } ], "paths": { "/ping": { "get": { "responses": { "200": { "description": "pong" } }, "deprecated": false } } } } ``` ## Take advantage of Pydantic Pydantic is a great tool. It allows you to use object / dict / mixed data for input. The following examples give the same OpenAPI result as above: ```python from openapi_pydantic import parse_obj, OpenAPI, PathItem, Response # Construct OpenAPI from dict, inferring the correct schema version open_api = parse_obj({ "openapi": "3.1.1", "info": {"title": "My own API", "version": "v0.0.1"}, "paths": { "/ping": { "get": {"responses": {"200": {"description": "pong"}}} } }, }) # Construct OpenAPI v3.1 schema from dict # Note: for Pydantic 1.x, replace `model_validate` with `parse_obj` open_api = OpenAPI.model_validate({ "info": {"title": "My own API", "version": "v0.0.1"}, "paths": { "/ping": { "get": {"responses": {"200": {"description": "pong"}}} } }, }) # Construct OpenAPI with mix of dict/object # Note: for Pydantic 1.x, replace `model_validate` with `parse_obj` open_api = OpenAPI.model_validate({ "info": {"title": "My own API", "version": "v0.0.1"}, "paths": { "/ping": PathItem( get={"responses": {"200": Response(description="pong")}} ) }, }) ``` ## Use Pydantic classes as schema - The [Schema Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.4.md#schemaObject) in OpenAPI has definitions and tweaks in JSON Schema, which are hard to comprehend and define a good data class - Pydantic already has a good way to [create JSON schema](https://pydantic-docs.helpmanual.io/usage/schema/). Let's not reinvent the wheel. The approach to deal with this: 1. Use `PydanticSchema` objects to represent the `Schema` in `OpenAPI` object 2. Invoke `construct_open_api_with_schema_class` to resolve the JSON schemas and references ```python from pydantic import BaseModel, Field from openapi_pydantic import OpenAPI from openapi_pydantic.util import PydanticSchema, construct_open_api_with_schema_class def construct_base_open_api() -> OpenAPI: # Note: for Pydantic 1.x, replace `model_validate` with `parse_obj` return OpenAPI.model_validate({ "info": {"title": "My own API", "version": "v0.0.1"}, "paths": { "/ping": { "post": { "requestBody": {"content": {"application/json": { "schema": PydanticSchema(schema_class=PingRequest) }}}, "responses": {"200": { "description": "pong", "content": {"application/json": { "schema": PydanticSchema(schema_class=PingResponse) }}, }}, } } }, }) class PingRequest(BaseModel): """Ping Request""" req_foo: str = Field(description="foo value of the request") req_bar: str = Field(description="bar value of the request") class PingResponse(BaseModel): """Ping response""" resp_foo: str = Field(description="foo value of the response") resp_bar: str = Field(description="bar value of the response") open_api = construct_base_open_api() open_api = construct_open_api_with_schema_class(open_api) # print the result openapi.json # Note: for Pydantic 1.x, replace `model_dump_json` with `json` print(open_api.model_dump_json(by_alias=True, exclude_none=True, indent=2)) ``` Result: ```json { "openapi": "3.1.1", "info": { "title": "My own API", "version": "v0.0.1" }, "servers": [ { "url": "/" } ], "paths": { "/ping": { "post": { "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PingRequest" } } }, "required": false }, "responses": { "200": { "description": "pong", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PingResponse" } } } } }, "deprecated": false } } }, "components": { "schemas": { "PingRequest": { "title": "PingRequest", "required": [ "req_foo", "req_bar" ], "type": "object", "properties": { "req_foo": { "title": "Req Foo", "type": "string", "description": "foo value of the request" }, "req_bar": { "title": "Req Bar", "type": "string", "description": "bar value of the request" } }, "description": "Ping Request" }, "PingResponse": { "title": "PingResponse", "required": [ "resp_foo", "resp_bar" ], "type": "object", "properties": { "resp_foo": { "title": "Resp Foo", "type": "string", "description": "foo value of the response" }, "resp_bar": { "title": "Resp Bar", "type": "string", "description": "bar value of the response" } }, "description": "Ping response" } } } } ``` ## Notes ### Use of OpenAPI.model_dump() / OpenAPI.model_dump_json() / OpenAPI.json() / OpenAPI.dict() When using `OpenAPI.model_dump()` / `OpenAPI.model_dump_json()` / `OpenAPI.json()` / `OpenAPI.dict()` functions, the arguments `by_alias=True, exclude_none=True` have to be in place. Otherwise the resulting json will not fit the OpenAPI standard. ```python # OK (Pydantic 2) open_api.model_dump_json(by_alias=True, exclude_none=True, indent=2) # OK (Pydantic 1) open_api.json(by_alias=True, exclude_none=True, indent=2) # Not good open_api.model_dump_json(indent=2) open_api.json(indent=2) ``` More info about field aliases: | OpenAPI version | Field alias info | | --------------- | ---------------- | | 3.1 | [here](https://github.com/mike-oakley/openapi-pydantic/blob/main/openapi_pydantic/v3/v3_1/README.md#alias) | | 3.0 | [here](https://github.com/mike-oakley/openapi-pydantic/blob/main/openapi_pydantic/v3/v3_0/README.md#alias) | ### Non-pydantic schema types Some schema types are not implemented as pydantic classes. Please refer to the following for more info: | OpenAPI version | Non-pydantic schema type info | | --------------- | ----------------------------- | | 3.1 | [here](https://github.com/mike-oakley/openapi-pydantic/blob/main/openapi_pydantic/v3/v3_1/README.md#non-pydantic-schema-types) | | 3.0 | [here](https://github.com/mike-oakley/openapi-pydantic/blob/main/openapi_pydantic/v3/v3_0/README.md#non-pydantic-schema-types) | ### Use OpenAPI 3.0 instead of 3.1 Some UI renderings (e.g. Swagger) still do not support OpenAPI 3.1.x. The old 3.0.x version is available by importing from different paths: ```python from openapi_pydantic.v3.v3_0 import OpenAPI, ... from openapi_pydantic.v3.v3_0.util import PydanticSchema, construct_open_api_with_schema_class ``` ### Pydantic version compatibility Compatibility with both major versions of Pydantic (1.8+ and 2.*) is mostly achieved using a module called `compat.py`. It detects the installed version of Pydantic and exports version-specific symbols for use by the rest of the package. It also provides all symbols necessary for type checking. The `compat.py` module is not intended to be imported by other packages, but other packages may find it helpful as an example of how to span major versions of Pydantic. ## Credits This library is based from the original implementation by Kuimono of [OpenAPI Schema Pydantic](https://github.com/kuimono/openapi-schema-pydantic) which is no longer actively maintained. ## License [MIT License](https://github.com/mike-oakley/openapi-pydantic/blob/main/LICENSE)