question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[FEATURE] Optionally provide openapi spec as YAML

See original GitHub issue

Is your feature request related to a problem

FastAPI can generate the OpenAPI spec as JSON. It would be nice if that could (optionally) be converted to YAML by FastAPI, because the spec is usually read by humans and sometimes, dev policies might require that the spec is made available in YAML format.

The solution you would like

You can already specifiy the openapi_url when creating a FastAPI instance. When the provided url ens with .yaml, a yaml should be generated instead of a JSON:

app = FastAPI(openapi_url='/openapi.yaml')

The method FastAPI.setup() would need to be patched to something like this:

    def setup(self) -> None:
        if self.openapi_url:
            if self.openapi_url.endswith('.yaml'):
                async def openapi(req: Request) -> Response:
                    yaml_str = StringIO()
                    yaml = YAML()
                    yaml.indent(mapping=2, sequence=4, offset=2)
                    yaml.dump(self.openapi(), yaml_str)
                    return Response(yaml_str.getvalue(), media_type='text/yaml')
            else:
                async def openapi(req: Request) -> JSONResponse:
                    return JSONResponse(self.openapi())

            self.add_route(self.openapi_url, openapi, include_in_schema=False)
            ...

This would add the dependency ruamel.yaml, but that could be made optional.

Describe alternatives you’ve considered

Manually converting the JSON file to YAML evertime the API changes.

Additional context

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:11 (3 by maintainers)

github_iconTop GitHub Comments

38reactions
hjouklcommented, Jul 16, 2020

Thanks a lot for sharing @sscherfke.

@tiangolo: Does it still hold true that an unwanted additional dependency would be needed for this? As per https://fastapi.tiangolo.com/#optional-dependencies fastapi has an (optional) dependency on pyyaml anyway, so this could also be done with pyaml, not needing ruamel. So maybe this could indeed be provided out-of-the-box conditionally, if pyyaml is installed, and automagically get linked in the /doc and /redoc pages…

In case anyone stumbles upon this (like myself) here’s a simple variant without inheritance:


from fastapi import FastAPI
from fastapi.responses import Response
import functools
import io
from pydantic import BaseModel
import yaml


# data model
class HelloResponse(BaseModel):
    message: str


app = FastAPI()

# add endpoints
# additional yaml version of openapi.json
@app.get('/openapi.yaml', include_in_schema=False)
@functools.lru_cache()
def read_openapi_yaml() -> Response:
    openapi_json= app.openapi()
    yaml_s = io.StringIO()
    yaml.dump(openapi_json, yaml_s)
    return Response(yaml_s.getvalue(), media_type='text/yaml')


@app.get("/hello", response_model=HelloResponse)
def read_hello():
    return {"message": "Hello, world!"}


@app.get("/hello/{name}", response_model=HelloResponse)
def read_hello_name(name: str):
    return {"message": f"Hello, {name}!"}

The lru_cache mimicks app.openapi() once-only schema generation for the json-to-yaml conversion. I don’t know if this would work if there’s any real async going on here (doesn’t look like it from what I can see in app.openapi(), but I’m basically an async noob 😃).

Here’s a gist that additionally hooks the .yaml OAS schema into the fastapi redoc page, instead of the normal .json: https://gist.github.com/hjoukl/790f95128e431396bcabf5ed39a5610b

10reactions
maresbcommented, Aug 22, 2020

Thanks @hjoukl for sharing your code, it definitely saved me time!

I would recommend using yaml.dump with the additional options sort_keys=False so that things are arranged in a coherent order and allow_unicode=True so that special characters don’t get mangled.

I wrote the following additional code which displays a pretty-formatted YAML specification in the browser from /openapi.html:

from fastapi.responses import HTMLResponse

@app.get('/openapi.html', include_in_schema=False)
def pretty_yaml(request: Request) -> Response:
    return HTMLResponse(r"""

<!DOCTYPE html>
<!-- CDN links were obtained from https://cdnjs.com/libraries/prism/1.21.0 -->
<html>
<head>
  <meta charset="utf-8" />
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.21.0/themes/prism.css" integrity="sha512-jtWR3pdYjGwfw9df601YF6uGrKdhXV37c+/6VNzNctmrXoO0nkgHcS03BFxfkWycOa2P2Nw9Y9PCT9vjG9jkVg==" crossorigin="anonymous" />
</head>
<body>
<header data-plugin-header="file-highlight"></header>
  <pre data-src="openapi.yaml"></pre>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.21.0/components/prism-core.min.js" integrity="sha512-hqRrGU7ys5tkcqxx5FIZTBb7PkO2o3mU6U5+qB9b55kgMlBUT4J2wPwQfMCxeJW1fC8pBxuatxoH//z0FInhrA==" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.21.0/plugins/autoloader/prism-autoloader.min.js" integrity="sha512-ROhjG07IRaPZsryG77+MVyx3ZT5q3sGEGENoGItwc9xgvx+dl+s3D8Ob1zPdbl/iKklMKp7uFemLJFDRw0bvig==" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.21.0/plugins/file-highlight/prism-file-highlight.min.js" integrity="sha512-DP0E4atVbD/CgElRtPoPSTEVDN7W5Xuy/JkOOwxAVhUePY30VMGNt63HLEQolVUc0XpIft8s+xEPVpWB+KX0VA==" crossorigin="anonymous"></script>
</body>
</html>

""")
Read more comments on GitHub >

github_iconTop Results From Across the Web

OpenAPI Specification - Version 3.0.3
The OpenAPI Specification (OAS) defines a standard, language-agnostic interface to RESTful APIs which allows both humans and computers to discover and ...
Read more >
Working in YAML (OpenAPI tutorial) | Documenting APIs
The values for each key can optionally be enclosed in quotation marks. If your value has a colon or quotation mark in it,...
Read more >
OpenAPI Specification v3.1.0 | Introduction, Definitions, & ...
A URI that points to the literal example. This provides the capability to reference examples that cannot easily be included in JSON or...
Read more >
OpenAPI 3.0 Tutorial | SwaggerHub Documentation
The latest version of OpenAPI is 3.0. OpenAPI definitions can be written in JSON or YAML. We recommend YAML, because it is easier...
Read more >
OpenAPI Definition & Online Tools | Open API Standards List
API architects can review upcoming API changes and offer feedback. ... A document written to the OpenAPI specification can use either JSON or...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found