]> git.ipfire.org Git - thirdparty/fastapi/fastapi.git/commitdiff
:sparkles: Add docs/tests extending OpenAPI (#126)
authorSebastián Ramírez <tiangolo@gmail.com>
Sat, 30 Mar 2019 15:53:44 +0000 (19:53 +0400)
committerGitHub <noreply@github.com>
Sat, 30 Mar 2019 15:53:44 +0000 (19:53 +0400)
docs/img/tutorial/extending-openapi/image01.png [new file with mode: 0644]
docs/src/extending_openapi/tutorial001.py [new file with mode: 0644]
docs/tutorial/extending-openapi.md [new file with mode: 0644]
mkdocs.yml
tests/test_tutorial/test_extending_openapi/__init__.py [new file with mode: 0644]
tests/test_tutorial/test_extending_openapi/test_tutorial001.py [new file with mode: 0644]

diff --git a/docs/img/tutorial/extending-openapi/image01.png b/docs/img/tutorial/extending-openapi/image01.png
new file mode 100644 (file)
index 0000000..26d1329
Binary files /dev/null and b/docs/img/tutorial/extending-openapi/image01.png differ
diff --git a/docs/src/extending_openapi/tutorial001.py b/docs/src/extending_openapi/tutorial001.py
new file mode 100644 (file)
index 0000000..561e958
--- /dev/null
@@ -0,0 +1,28 @@
+from fastapi import FastAPI
+from fastapi.openapi.utils import get_openapi
+
+app = FastAPI()
+
+
+@app.get("/items/")
+async def read_items():
+    return [{"name": "Foo"}]
+
+
+def custom_openapi():
+    if app.openapi_schema:
+        return app.openapi_schema
+    openapi_schema = get_openapi(
+        title="Custom title",
+        version="2.5.0",
+        description="This is a very custom OpenAPI schema",
+        routes=app.routes,
+    )
+    openapi_schema["info"]["x-logo"] = {
+        "url": "https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png"
+    }
+    app.openapi_schema = openapi_schema
+    return app.openapi_schema
+
+
+app.openapi = custom_openapi
diff --git a/docs/tutorial/extending-openapi.md b/docs/tutorial/extending-openapi.md
new file mode 100644 (file)
index 0000000..585f293
--- /dev/null
@@ -0,0 +1,90 @@
+!!! warning
+    This is a rather advanced feature. You probably can skip it.
+
+    If you are just following the tutorial - user guide, you can probably skip this section.
+
+    If you already know that you need to modify the generated OpenAPI schema, continue reading.
+
+
+There are some cases where you might need to modify the generated OpenAPI schema.
+
+In this section you will see how.
+
+## The normal process
+
+The normal (default) process, is as follows.
+
+A `FastAPI` application (instance) has an `.openapi()` method that is expected to return the OpenAPI schema.
+
+As part of the application object creation, a *path operation* for `/openapi.json` (or for whatever you set your `openapi_url`) is registered.
+
+It just returns a JSON response with the result of the application's `.openapi()` method.
+
+By default, what the method `.openapi()` does is check the property `.openapi_schema` to see if it has contents and return them.
+
+If it doesn't, it generates them using the utility function at `fastapi.openapi.utils.get_openapi`.
+
+And that function `get_openapi()` receives as parameters:
+
+* `title`: The OpenAPI title, shown in the docs.
+* `version`: The version of your API, e.g. `2.5.0`.
+* `openapi_version`: The version of the OpenAPI specification used. By default, the latest: `3.0.2`.
+* `description`: The description of your API.
+* `routes`: A list of routes, these are each of the registered *path operations*. They are taken from `app.routes`.
+* `openapi_prefix`: The URL prefix to be used in your OpenAPI.
+
+## Overriding the defaults
+
+Using the information above, you can use the same utility function to generate the OpenAPI schema and override each part that you need.
+
+For example, let's add <a href="https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md#x-logo" target="_blank">ReDoc's OpenAPI extension to include a custom logo</a>.
+
+### Normal **FastAPI**
+
+First, write all your **FastAPI** application as normally:
+
+```Python hl_lines="1 4 7 8 9"
+{!./src/extending_openapi/tutorial001.py!}
+```
+
+### Generate the OpenAPI schema
+
+Then, use the same utility function to generate the OpenAPI schema, inside a `custom_openapi()` function:
+
+```Python hl_lines="2 15 16 17 18 19 20"
+{!./src/extending_openapi/tutorial001.py!}
+```
+
+### Modify the OpenAPI schema
+
+Now you can add the ReDoc extension, adding a custom `x-logo` to the `info` "object" in the OpenAPI schema:
+
+```Python hl_lines="21 22 23"
+{!./src/extending_openapi/tutorial001.py!}
+```
+
+### Cache the OpenAPI schema
+
+You can use the property `.openapi_schema` as a "cache", to store your generated schema.
+
+That way, your application won't have to generate the schema every time a user opens your API docs.
+
+It will be generated only once, and then the same cached schema will be used for the next requests.
+
+```Python hl_lines="13 14 24 25"
+{!./src/extending_openapi/tutorial001.py!}
+```
+
+### Override the method
+
+Now you can replace the `.openapi()` method with your new function.
+
+```Python hl_lines="28"
+{!./src/extending_openapi/tutorial001.py!}
+```
+
+### Check it
+
+Once you go to <a href="http://127.0.0.1:8000/redoc" target="_blank">http://127.0.0.1:8000/redoc</a> you will see that you are using your custom logo (in this example, **FastAPI**'s logo):
+
+<img src="/img/tutorial/extending-openapi/image01.png">
index f550d486784063f82e913637f19f9817a3f2d0aa..4edc575f249d9ddeb0cc23edabc183d493af2ea5 100644 (file)
@@ -67,6 +67,7 @@ nav:
         - WebSockets: 'tutorial/websockets.md'
         - 'Events: startup - shutdown': 'tutorial/events.md'
         - Debugging: 'tutorial/debugging.md'
+        - Extending OpenAPI: 'tutorial/extending-openapi.md'
     - Concurrency and async / await: 'async.md'
     - Deployment: 'deployment.md'
     - Project Generation - Template: 'project-generation.md'
diff --git a/tests/test_tutorial/test_extending_openapi/__init__.py b/tests/test_tutorial/test_extending_openapi/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/test_tutorial/test_extending_openapi/test_tutorial001.py b/tests/test_tutorial/test_extending_openapi/test_tutorial001.py
new file mode 100644 (file)
index 0000000..8600096
--- /dev/null
@@ -0,0 +1,44 @@
+from starlette.testclient import TestClient
+
+from extending_openapi.tutorial001 import app
+
+client = TestClient(app)
+
+openapi_schema = {
+    "openapi": "3.0.2",
+    "info": {
+        "title": "Custom title",
+        "version": "2.5.0",
+        "description": "This is a very custom OpenAPI schema",
+        "x-logo": {"url": "https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png"},
+    },
+    "paths": {
+        "/items/": {
+            "get": {
+                "responses": {
+                    "200": {
+                        "description": "Successful Response",
+                        "content": {"application/json": {"schema": {}}},
+                    }
+                },
+                "summary": "Read Items Get",
+                "operationId": "read_items_items__get",
+            }
+        }
+    },
+}
+
+
+def test_openapi_schema():
+    response = client.get("/openapi.json")
+    assert response.status_code == 200
+    assert response.json() == openapi_schema
+    response = client.get("/openapi.json")
+    assert response.status_code == 200
+    assert response.json() == openapi_schema
+
+
+def test():
+    response = client.get("/items/")
+    assert response.status_code == 200
+    assert response.json() == [{"name": "Foo"}]