Compare commits

..

14 Commits

Author SHA1 Message Date
Sebastián Ramírez
072c2bc7f9 🔖 Release version 0.56.0 2020-06-12 00:22:17 +02:00
Sebastián Ramírez
da7826b0eb 📝 Update release notes 2020-06-12 00:05:17 +02:00
Ingmar Steen
2f478eeca6 Add support for ASGI root_path for openapi docs (#1199)
* Use ASGI root_path when it is provided and openapi_prefix is empty.

* Strip trailing slashes from root_path.

* Please mypy.

* Fix extending openapi test.

* 📝 Add docs and tutorial for using root_path behind a proxy

* ♻️ Refactor application root_path logic, use root_path, deprecate openapi_prefix

*  Add tests for Behind a Proxy with root_path

* ♻️ Refactor test

* 📝 Update/add docs for Sub-applications and Behind a Proxy

* 📝 Update Extending OpenAPI with openapi_prefix parameter

*  Add test for deprecated openapi_prefix

Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2020-06-11 23:53:19 +02:00
Sebastián Ramírez
543ef7753a 📝 Update release notes 2020-06-07 22:02:36 +02:00
Sebastián Ramírez
88a887329e 📝 Update help and issue templates (#1531)
* 📝 Update help docs: Gitter, issues, links

also fix Gitter tab padding

* 📝 Update new GitHub issue templates

* 📝 Add note about extra help required for new issues
2020-06-07 22:00:15 +02:00
Sebastián Ramírez
8cfe254400 📝 Update release notes 2020-06-05 17:35:39 +02:00
Sebastián Ramírez
bfd46e562b 🔧 Update issue-manager GitHub action (#1520) 2020-06-05 17:34:43 +02:00
Sebastián Ramírez
a0e4d38bea 📝 Update release notes 2020-05-24 08:48:52 +02:00
Sebastián Ramírez
b0414b9929 📝 Add new links (#1467)
* 📝 Update opinions including Netflix and add format

* 📝 Add new external links

* 📝 Update README
2020-05-24 08:48:09 +02:00
Sebastián Ramírez
3b4413f9f5 📝 Update release notes 2020-05-24 07:47:19 +02:00
Xie Wei
374cdf29a9 🌐 Add Chinese translation for docs/python-types.md (#1197)
* Add Chinese tranlation for docs/python-types.md

* improve translation
2020-05-24 07:43:26 +02:00
Sebastián Ramírez
8d844bc5cf 🔖 Release version 0.55.1 2020-05-23 18:59:14 +02:00
Sebastián Ramírez
1092261ae1 📝 Update release notes 2020-05-23 18:59:14 +02:00
Sebastián Ramírez
5984233223 🐛 Fix Enum handling with their own schema definitions (#1463)
* 🐛 Fix extra support for enum with its own schema

*  Fix/update test for enum with its own schema

* 🐛 Fix type declarations

* 🔧 Update format and lint scripts to support locally installed Pydantic and Starlette

* 🐛 Add temporary type ignores while enum schemas are merged
2020-05-23 18:56:18 +02:00
36 changed files with 1219 additions and 316 deletions

View File

@@ -1,62 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: "[BUG]"
labels: bug
assignees: ''
---
### Describe the bug
Write here a clear and concise description of what the bug is.
### To Reproduce
Steps to reproduce the behavior with a minimum self-contained file.
Replace each part with your own scenario:
1. Create a file with:
```Python
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
```
3. Open the browser and call the endpoint `/`.
4. It returns a JSON with `{"Hello": "World"}`.
5. But I expected it to return `{"Hello": "Sara"}`.
### Expected behavior
Add a clear and concise description of what you expected to happen.
### Screenshots
If applicable, add screenshots to help explain your problem.
### Environment
- OS: [e.g. Linux / Windows / macOS]
- FastAPI Version [e.g. 0.3.0], get it with:
```bash
python -c "import fastapi; print(fastapi.__version__)"
```
- Python version, get it with:
```bash
python --version
```
### Additional context
Add any other context about the problem here.

View File

@@ -1,26 +1,104 @@
---
name: Feature request
about: Suggest an idea for this project
title: "[FEATURE]"
title: ""
labels: enhancement
assignees: ''
---
### Is your feature request related to a problem
### First check
Is your feature request related to a problem?
* [ ] I added a very descriptive title to this issue.
* [ ] I used the GitHub search to find a similar issue and didn't find it.
* [ ] I searched the FastAPI documentation, with the integrated search.
* [ ] I already searched in Google "How to X in FastAPI" and didn't find any information.
* [ ] I already read and followed all the tutorial in the docs and didn't find an answer.
* [ ] I already checked if it is not related to FastAPI but to [Pydantic](https://github.com/samuelcolvin/pydantic).
* [ ] I already checked if it is not related to FastAPI but to [Swagger UI](https://github.com/swagger-api/swagger-ui).
* [ ] I already checked if it is not related to FastAPI but to [ReDoc](https://github.com/Redocly/redoc).
* [ ] After submitting this, I commit to:
* Read open issues with questions until I find 2 issues where I can help someone and add a comment to help there.
* Or, I already hit the "watch" button in this repository to receive notifications and I commit to help at least 2 people that ask questions in the future.
* Implement a Pull Request for a confirmed bug.
Add a clear and concise description of what the problem is. Ex. I want to be able to [...] but I can't because [...]
<!--
I'm asking all this because answering questions and solving problems in GitHub issues consumes a lot of time. I end up not being able to add new features, fix bugs, review Pull Requests, etc. as fast as I wish because I have to spend too much time handling issues.
All that, on top of all the incredible help provided by a bunch of community members that give a lot of their time to come here and help others.
That's a lot of work they are doing, but if more FastAPI users came to help others like them just a little bit more, it would be much less effort for them (and you and me 😅).
-->
### Example
Here's a self-contained [minimal, reproducible, example](https://stackoverflow.com/help/minimal-reproducible-example) with my use case:
<!-- Replace the code below with your own self-contained, minimal, reproducible, example -->
```Python
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
```
### Description
<!-- Replace the content below with your own feature request -->
* Open the browser and call the endpoint `/`.
* It returns a JSON with `{"Hello": "World"}`.
* I would like it to have an extra parameter to teleport me to the moon and back.
### The solution you would like
Add a clear and concise description of what you want to happen.
<!-- Replace this with your own content -->
I would like it to have a `teleport_to_moon` parameter that defaults to `False`, and can be set to `True` to teleport me:
```Python
from fastapi import FastAPI
app = FastAPI()
@app.get("/", teleport_to_moon=True)
def read_root():
return {"Hello": "World"}
```
### Describe alternatives you've considered
Add a clear and concise description of any alternative solutions or features you've considered.
<!-- Replace this with your own ideas -->
To wait for Space X moon travel plans to drop down long after they release them. But I would rather teleport.
### Environment
* OS: [e.g. Linux / Windows / macOS]:
* FastAPI Version [e.g. 0.3.0]:
To know the FastAPI version use:
```bash
python -c "import fastapi; print(fastapi.__version__)"
```
* Python version:
To know the Python version use:
```bash
python --version
```
### Additional context
Add any other context or screenshots about the feature request here.
<!-- Add any other context or screenshots about the question here. -->

View File

@@ -1,24 +1,81 @@
---
name: Question
about: Ask a question
title: "[QUESTION]"
name: Question or Problem
about: Ask a question or ask about a problem
title: ""
labels: question
assignees: ''
assignees: ""
---
### First check
* [ ] I added a very descriptive title to this issue.
* [ ] I used the GitHub search to find a similar issue and didn't find it.
* [ ] I searched the FastAPI documentation, with the integrated search.
* [ ] I already searched in Google "How to X in FastAPI" and didn't find any information.
* [ ] I already read and followed all the tutorial in the docs and didn't find an answer.
* [ ] I already checked if it is not related to FastAPI but to [Pydantic](https://github.com/samuelcolvin/pydantic).
* [ ] I already checked if it is not related to FastAPI but to [Swagger UI](https://github.com/swagger-api/swagger-ui).
* [ ] I already checked if it is not related to FastAPI but to [ReDoc](https://github.com/Redocly/redoc).
* [ ] After submitting this, I commit to one of:
* Read open issues with questions until I find 2 issues where I can help someone and add a comment to help there.
* I already hit the "watch" button in this repository to receive notifications and I commit to help at least 2 people that ask questions in the future.
* Implement a Pull Request for a confirmed bug.
<!--
I'm asking all this because answering questions and solving problems in GitHub issues consumes a lot of time. I end up not being able to add new features, fix bugs, review Pull Requests, etc. as fast as I wish because I have to spend too much time handling issues.
All that, on top of all the incredible help provided by a bunch of community members that give a lot of their time to come here and help others.
That's a lot of work they are doing, but if more FastAPI users came to help others like them just a little bit more, it would be much less effort for them (and you and me 😅).
-->
### Example
Here's a self-contained, [minimal, reproducible, example](https://stackoverflow.com/help/minimal-reproducible-example) with my use case:
<!-- Replace the code below with your own self-contained, minimal, reproducible, example, if I (or someone) can copy it, run it, and see it right away, there's a much higher chance I (or someone) will be able to help you -->
```Python
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
```
### Description
How can I [...]?
<!-- Replace the content below with your own problem, question, or error -->
Is it possible to [...]?
* Open the browser and call the endpoint `/`.
* It returns a JSON with `{"Hello": "World"}`.
* But I expected it to return `{"Hello": "Sara"}`.
### Environment
* OS: [e.g. Linux / Windows / macOS]:
* FastAPI Version [e.g. 0.3.0]:
To know the FastAPI version use:
```bash
python -c "import fastapi; print(fastapi.__version__)"
```
* Python version:
To know the Python version use:
```bash
python --version
```
### Additional context
Add any other context or screenshots about the question here.
<!-- Add any other context or screenshots about the question here. -->

View File

@@ -1,15 +1,24 @@
name: Issue Manager
on:
schedule:
- cron: "0 0 * * *"
- cron: "0 0 * * *"
issue_comment:
types:
- created
- edited
issues:
types:
- labeled
jobs:
issue-manager:
runs-on: ubuntu-latest
steps:
- uses: tiangolo/issue-manager@master
with:
token: ${{ secrets.GITHUB_TOKEN }}
config: >
- uses: tiangolo/issue-manager@0.2.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
config: >
{
"answered": {
"users": ["tiangolo", "dmontagu"],

View File

@@ -45,38 +45,44 @@ The key features are:
## Opinions
"*[...] I'm using **FastAPI** a ton these days. [...] I'm actually planning to use it for all of my team's **ML services at Microsoft**. Some of them are getting integrated into the core **Windows** product and some **Office** products.*"
"_[...] I'm using **FastAPI** a ton these days. [...] I'm actually planning to use it for all of my team's **ML services at Microsoft**. Some of them are getting integrated into the core **Windows** product and some **Office** products._"
<div style="text-align: right; margin-right: 10%;">Kabir Khan - <strong>Microsoft</strong> <a href="https://github.com/tiangolo/fastapi/pull/26" target="_blank"><small>(ref)</small></a></div>
---
"*Im over the moon excited about **FastAPI**. Its so fun!*"
"_We adopted the **FastAPI** library to spawn a **REST** server that can be queried to obtain **predictions**. [for Ludwig]_"
<div style="text-align: right; margin-right: 10%;">Piero Molino, Yaroslav Dudin, and Sai Sumanth Miryala - <strong>Uber</strong> <a href="https://eng.uber.com/ludwig-v0-2/" target="_blank"><small>(ref)</small></a></div>
---
"_**Netflix** is pleased to announce the open-source release of our **crisis management** orchestration framework: **Dispatch**! [built with **FastAPI**]_"
<div style="text-align: right; margin-right: 10%;">Kevin Glisson, Marc Vilanova, Forest Monsen - <strong>Netflix</strong> <a href="https://netflixtechblog.com/introducing-dispatch-da4b8a2a8072" target="_blank"><small>(ref)</small></a></div>
---
"_Im over the moon excited about **FastAPI**. Its so fun!_"
<div style="text-align: right; margin-right: 10%;">Brian Okken - <strong><a href="https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855" target="_blank">Python Bytes</a> podcast host</strong> <a href="https://twitter.com/brianokken/status/1112220079972728832" target="_blank"><small>(ref)</small></a></div>
---
"*Honestly, what you've built looks super solid and polished. In many ways, it's what I wanted **Hug** to be - it's really inspiring to see someone build that.*"
"_Honestly, what you've built looks super solid and polished. In many ways, it's what I wanted **Hug** to be - it's really inspiring to see someone build that._"
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong><a href="http://www.hug.rest/" target="_blank">Hug</a> creator</strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div>
---
"*If you're looking to learn one **modern framework** for building REST APIs, check out **FastAPI** [...] It's fast, easy to use and easy to learn [...]*"
"_If you're looking to learn one **modern framework** for building REST APIs, check out **FastAPI** [...] It's fast, easy to use and easy to learn [...]_"
"*We've switched over to **FastAPI** for our **APIs** [...] I think you'll like it [...]*"
"_We've switched over to **FastAPI** for our **APIs** [...] I think you'll like it [...]_"
<div style="text-align: right; margin-right: 10%;">Ines Montani - Matthew Honnibal - <strong><a href="https://explosion.ai" target="_blank">Explosion AI</a> founders - <a href="https://spacy.io" target="_blank">spaCy</a> creators</strong> <a href="https://twitter.com/_inesmontani/status/1144173225322143744" target="_blank"><small>(ref)</small></a> - <a href="https://twitter.com/honnibal/status/1144031421859655680" target="_blank"><small>(ref)</small></a></div>
---
"*We adopted the **FastAPI** library to spawn a **REST** server that can be queried to obtain **predictions**. [for Ludwig]*"
<div style="text-align: right; margin-right: 10%;">Piero Molino, Yaroslav Dudin, and Sai Sumanth Miryala - <strong>Uber</strong> <a href="https://eng.uber.com/ludwig-v0-2/" target="_blank"><small>(ref)</small></a></div>
---
## **Typer**, the FastAPI of CLIs
<a href="https://typer.tiangolo.com" target="_blank"><img src="https://typer.tiangolo.com/img/logo-margin/logo-margin-vector.svg" style="width: 20%;"></a>

View File

@@ -0,0 +1,281 @@
# Behind a Proxy
In some situations, you might need to use a **proxy** server like Traefik or Nginx with a configuration that adds an extra path prefix that is not seen by your application.
In these cases you can use `root_path` to configure your application.
The `root_path` is a mechanism provided by the ASGI specification (that FastAPI is built on, through Starlette).
The `root_path` is used to handle these specific cases.
And it's also used internally when mounting sub-applications.
## Proxy with a stripped path prefix
Having a proxy with a stripped path prefix, in this case, means that you could declare a path at `/app` in your code, but then, you add a layer on top (the proxy) that would put your **FastAPI** application under a path like `/api/v1`.
In this case, the original path `/app` would actually be served at `/api/v1/app`.
Even though all your code is written assuming there's just `/app`.
And the proxy would be **"stripping"** the **path prefix** on the fly before transmitting the request to Uvicorn, keep your application convinced that it is serving at `/app`, so that you don't have to update all your code to include the prefix `/api/v1`.
Up to here, everything would work as normally.
But then, when you open the integrated docs UI (the frontend), it would expect to get the OpenAPI schema at `/openapi.json`, instead of `/api/v1/openapi.json`.
So, the frontend (that runs in the browser) would try to reach `/openapi.json` and wouldn't be able to get the OpenAPI schema.
Because we have a proxy with a path prefix of `/api/v1` for our app, the frontend needs to fetch the OpenAPI schema at `/api/v1/openapi.json`.
```mermaid
graph LR
browser("Browser")
proxy["Proxy on http://0.0.0.0:9999/api/v1/app"]
server["Server on http://127.0.0.1:8000/app"]
browser --> proxy
proxy --> server
```
!!! tip
The IP `0.0.0.0` is commonly used to mean that the program listens on all the IPs available in that machine/server.
The docs UI would also need that the JSON payload with the OpenAPI schema has the path defined as `/api/v1/app` (behind the proxy) instead of `/app`. For example, something like:
```JSON hl_lines="5"
{
"openapi": "3.0.2",
// More stuff here
"paths": {
"/api/v1/app": {
// More stuff here
}
}
}
```
In this example, the "Proxy" could be something like **Traefik**. And the server would be something like **Uvicorn**, running your FastAPI application.
### Providing the `root_path`
To achieve this, you can use the command line option `--root-path` like:
<div class="termy">
```console
$ uvicorn main:app --root-path /api/v1
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
If you use Hypercorn, it also has the option `--root-path`.
!!! note "Technical Details"
The ASGI specification defines a `root_path` for this use case.
And the `--root-path` command line option provides that `root_path`.
### Checking the current `root_path`
You can get the current `root_path` used by your application for each request, it is part of the `scope` dictionary (that's part of the ASGI spec).
Here we are including it in the message just for demonstration purposes.
```Python hl_lines="8"
{!../../../docs_src/behind_a_proxy/tutorial001.py!}
```
Then, if you start Uvicorn with:
<div class="termy">
```console
$ uvicorn main:app --root-path /api/v1
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
The response would be something like:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
### Setting the `root_path` in the FastAPI app
Alternatively, if you don't have a way to provide a command line option like `--root-path` or equivalent, you can set the `root_path` parameter when creating your FastAPI app:
```Python hl_lines="3"
{!../../../docs_src/behind_a_proxy/tutorial002.py!}
```
Passing the `root_path` to `FastAPI` would be the equivalent of passing the `--root-path` command line option to Uvicorn or Hypercorn.
### About `root_path`
Have in mind that the server (Uvicorn) won't use that `root_path` for anything else than passing it to the app.
But if you go with your browser to <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000/app</a> you will see the normal response:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
So, it won't expect to be accessed at `http://127.0.0.1:8000/api/v1/app`.
Uvicorn will expect the proxy to access Uvicorn at `http://127.0.0.1:8000/app`, and then it would be the proxy's responsibility to add the extra `/api/v1` prefix on top.
## About proxies with a stripped path prefix
Have in mind that a proxy with stripped path prefix is only one of the ways to configure it.
Probably in many cases the default will be that the proxy doesn't have a stripped path prefix.
In a case like that (without a stripped path prefix), the proxy would listen on something like `https://myawesomeapp.com`, and then if the browser goes to `https://myawesomeapp.com/api/v1/app` and your server (e.g. Uvicorn) listens on `http://127.0.0.1:8000` the proxy (without a stripped path prefix) would access Uvicorn at the same path: `http://127.0.0.1:8000/api/v1/app`.
## Testing locally with Traefik
You can easily run the experiment locally with a stripped path prefix using <a href="https://docs.traefik.io/" class="external-link" target="_blank">Traefik</a>.
<a href="https://github.com/containous/traefik/releases" class="external-link" target="_blank">Download Traefik</a>, it's a single binary, you can extract the compressed file and run it directly from the terminal.
Then create a file `traefik.toml` with:
```TOML hl_lines="3"
[entryPoints]
[entryPoints.http]
address = ":9999"
[providers]
[providers.file]
filename = "routes.toml"
```
This tells Traefik to listen on port 9999 and to use another file `routes.toml`.
!!! tip
We are using port 9999 instead of the standard HTTP port 80 so that you don't have to run it with admin (`sudo`) privileges.
Now create that other file `routes.toml`:
```TOML hl_lines="5 12 20"
[http]
[http.middlewares]
[http.middlewares.api-stripprefix.stripPrefix]
prefixes = ["/api/v1"]
[http.routers]
[http.routers.app-http]
entryPoints = ["http"]
service = "app"
rule = "PathPrefix(`/api/v1`)"
middlewares = ["api-stripprefix"]
[http.services]
[http.services.app]
[http.services.app.loadBalancer]
[[http.services.app.loadBalancer.servers]]
url = "http://127.0.0.1:8000"
```
This file configures Traefik to use the path prefix `/api/v1`.
And then it will redirect its requests to your Uvicorn running on `http://127.0.0.1:8000`.
Now start Traefik:
<div class="termy">
```console
$ ./traefik --configFile=traefik.toml
INFO[0000] Configuration loaded from file: /home/user/awesomeapi/traefik.toml
```
</div>
And now start your app with Uvicorn, using the `--root-path` option:
<div class="termy">
```console
$ uvicorn main:app --root-path /api/v1
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
### Check the responses
Now, if you go to the URL with the port for Uvicorn: <a href="http://127.0.0.1:8000/app" class="external-link" target="_blank">http://127.0.0.1:8000/app</a>, you will see the normal response:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
!!! tip
Notice that even though you are accessing it at `http://127.0.0.1:8000/app` it shows the `root_path` of `/api/v1`, taken from the option `--root-path`.
And now open the URL with the port for Traefik, including the path prefix: <a href="http://127.0.0.1:9999/api/v1/app" class="external-link" target="_blank">http://127.0.0.1:9999/api/vi/app</a>.
We get the same response:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
but this time at the URL with the prefix path provided by the proxy: `/api/v1`.
Of course, the idea here is that everyone would access the app through the proxy, so the version with the path prefix `/app/v1` is the "correct" one.
And the version without the path prefix (`http://127.0.0.1:8000/app`), provided by Uvicorn directly, would be exclusively for the _proxy_ (Traefik) to access it.
That demonstrates how the Proxy (Traefik) uses the path prefix and how the server (Uvicorn) uses the `root_path` from the option `--root-path`.
### Check the docs UI
But here's the fun part. ✨
The "official" way to access the app would be through the proxy with the path prefix that we defined. So, as we would expect, if you try the docs UI served by Uvicorn directly, without the path prefix in the URL, it won't work, because it expects to be accessed through the proxy.
You can check it at <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>:
<img src="/img/tutorial/behind-a-proxy/image01.png">
But if we access the docs UI at the "official" URL using the proxy, at `/api/v1/docs`, it works correctly! 🎉
Right as we wanted it. ✔️
This is because FastAPI uses this `root_path` internally to tell the docs UI to use the URL for OpenAPI with the path prefix provided by `root_path`.
You can check it at <a href="http://127.0.0.1:9999/api/v1/docs" class="external-link" target="_blank">http://127.0.0.1:9999/api/v1/docs</a>:
<img src="/img/tutorial/behind-a-proxy/image02.png">
## Mounting a sub-application
If you need to mount a sub-application (as described in [Sub Applications - Mounts](./sub-applications.md){.internal-link target=_blank}) while also using a proxy with `root_path`, you can do it normally, as you would expect.
FastAPI will internally use the `root_path` smartly, so it will just work. ✨

View File

@@ -52,15 +52,22 @@ First, write all your **FastAPI** application as normally:
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"
```Python hl_lines="2 15 16 17 18 19 20 21"
{!../../../docs_src/extending_openapi/tutorial001.py!}
```
!!! tip
The `openapi_prefix` will contain any prefix needed for the generated OpenAPI *path operations*.
FastAPI will automatically use the `root_path` to pass it in the `openapi_prefix`.
But the important thing is that your function should receive that parameter `openapi_prefix` and pass it along.
### 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"
```Python hl_lines="22 23 24"
{!../../../docs_src/extending_openapi/tutorial001.py!}
```
@@ -72,7 +79,7 @@ That way, your application won't have to generate the schema every time a user o
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"
```Python hl_lines="13 14 25 26"
{!../../../docs_src/extending_openapi/tutorial001.py!}
```
@@ -80,7 +87,7 @@ It will be generated only once, and then the same cached schema will be used for
Now you can replace the `.openapi()` method with your new function.
```Python hl_lines="28"
```Python hl_lines="29"
{!../../../docs_src/extending_openapi/tutorial001.py!}
```

View File

@@ -1,100 +0,0 @@
# Sub Applications - Behind a Proxy, Mounts
There are at least two situations where you could need to create your **FastAPI** application using some specific paths.
But then you need to set them up to be served with a path prefix.
It could happen if you have a:
* **Proxy** server.
* You are "**mounting**" a FastAPI application inside another FastAPI application (or inside another ASGI application, like Starlette).
## Proxy
Having a proxy in this case means that you could declare a path at `/app`, but then, you could need to add a layer on top (the Proxy) that would put your **FastAPI** application under a path like `/api/v1`.
In this case, the original path `/app` will actually be served at `/api/v1/app`.
Even though your application "thinks" it is serving at `/app`.
And the Proxy could be re-writing the path "on the fly" to keep your application convinced that it is serving at `/app`.
Up to here, everything would work as normally.
But then, when you open the integrated docs, they would expect to get the OpenAPI schema at `/openapi.json`, instead of `/api/v1/openapi.json`.
So, the frontend (that runs in the browser) would try to reach `/openapi.json` and wouldn't be able to get the OpenAPI schema.
So, it's needed that the frontend looks for the OpenAPI schema at `/api/v1/openapi.json`.
And it's also needed that the returned JSON OpenAPI schema has the defined path at `/api/v1/app` (behind the proxy) instead of `/app`.
---
For these cases, you can declare an `openapi_prefix` parameter in your `FastAPI` application.
See the section below, about "mounting", for an example.
## Mounting a **FastAPI** application
"Mounting" means adding a complete "independent" application in a specific path, that then takes care of handling all the sub-paths.
You could want to do this if you have several "independent" applications that you want to separate, having their own independent OpenAPI schema and user interfaces.
### Top-level application
First, create the main, top-level, **FastAPI** application, and its *path operations*:
```Python hl_lines="3 6 7 8"
{!../../../docs_src/sub_applications/tutorial001.py!}
```
### Sub-application
Then, create your sub-application, and its *path operations*.
This sub-application is just another standard FastAPI application, but this is the one that will be "mounted".
When creating the sub-application, use the parameter `openapi_prefix`. In this case, with a prefix of `/subapi`:
```Python hl_lines="11 14 15 16"
{!../../../docs_src/sub_applications/tutorial001.py!}
```
### Mount the sub-application
In your top-level application, `app`, mount the sub-application, `subapi`.
Here you need to make sure you use the same path that you used for the `openapi_prefix`, in this case, `/subapi`:
```Python hl_lines="11 19"
{!../../../docs_src/sub_applications/tutorial001.py!}
```
## Check the automatic API docs
Now, run `uvicorn`, if your file is at `main.py`, it would be:
<div class="termy">
```console
$ uvicorn main:app --reload
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
And open the docs at <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
You will see the automatic API docs for the main app, including only its own paths:
<img src="/img/tutorial/sub-applications/image01.png">
And then, open the docs for the sub-application, at <a href="http://127.0.0.1:8000/subapi/docs" class="external-link" target="_blank">http://127.0.0.1:8000/subapi/docs</a>.
You will see the automatic API docs for the sub-application, including only its own sub-paths, with their correct prefix:
<img src="/img/tutorial/sub-applications/image02.png">
If you try interacting with any of the two user interfaces, they will work, because the browser will be able to talk to the correct path (or sub-path).

View File

@@ -0,0 +1,73 @@
# Sub Applications - Mounts
If you need to have two independent FastAPI applications, with their own independent OpenAPI and their own docs UIs, you can have a main app and "mount" one (or more) sub-application(s).
## Mounting a **FastAPI** application
"Mounting" means adding a completely "independent" application in a specific path, that then takes care of handling all everything under that path, with the _path operations_ declared in that sub-application.
### Top-level application
First, create the main, top-level, **FastAPI** application, and its *path operations*:
```Python hl_lines="3 6 7 8"
{!../../../docs_src/sub_applications/tutorial001.py!}
```
### Sub-application
Then, create your sub-application, and its *path operations*.
This sub-application is just another standard FastAPI application, but this is the one that will be "mounted":
```Python hl_lines="11 14 15 16"
{!../../../docs_src/sub_applications/tutorial001.py!}
```
### Mount the sub-application
In your top-level application, `app`, mount the sub-application, `subapi`.
In this case, it will be mounted at the path `/subapi`:
```Python hl_lines="11 19"
{!../../../docs_src/sub_applications/tutorial001.py!}
```
### Check the automatic API docs
Now, run `uvicorn` with the main app, if your file is `main.py`, it would be:
<div class="termy">
```console
$ uvicorn main:app --reload
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
And open the docs at <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
You will see the automatic API docs for the main app, including only its own _path operations_:
<img src="/img/tutorial/sub-applications/image01.png">
And then, open the docs for the sub-application, at <a href="http://127.0.0.1:8000/subapi/docs" class="external-link" target="_blank">http://127.0.0.1:8000/subapi/docs</a>.
You will see the automatic API docs for the sub-application, including only its own _path operations_, all under the correct sub-path prefix `/subapi`:
<img src="/img/tutorial/sub-applications/image02.png">
If you try interacting with any of the two user interfaces, they will work correctly, because the browser will be able to talk to each specific app or sub-app.
### Technical Details: `root_path`
When you mount a sub-application as described above, FastAPI will take care of communicating the mount path for the sub-application using a mechanism from the ASGI specification called a `root_path`.
That way, the sub-application will know to use that path prefix for the docs UI.
And the sub-application could also have its own mounted sub-applications and everything would work correctly, because FastAPI handles all these `root_path`s automatically.
You will learn more about the `root_path` and how to use it explicitly in the section about [Behind a Proxy](./behind-a-proxy.md){.internal-link target=_blank}.

View File

@@ -11,3 +11,8 @@ a.internal-link::after {
*/
content: "\00A0↪";
}
/* Give space to lower icons so Gitter chat doesn't get on top of them */
.md-footer-meta {
padding-bottom: 2em;
}

View File

@@ -61,6 +61,23 @@ Here's an incomplete list of some of them.
* <a href="https://iwpnd.pw/articles/2020-03/apache-kafka-fastapi-geostream" class="external-link" target="_blank">Apache Kafka producer and consumer with FastAPI and aiokafka</a> by <a href="https://iwpnd.pw" class="external-link" target="_blank">Benjamin Ramser</a>.
* <a href="https://wuilly.com/2019/10/real-time-notifications-with-python-and-postgres/" class="external-link" target="_blank">Real-time Notifications with Python and Postgres</a> by <a href="https://wuilly.com/" class="external-link" target="_blank">Guillermo Cruz</a>.
* <a href="https://dev.to/paurakhsharma/microservice-in-python-using-fastapi-24cc" class="external-link" target="_blank">Microservice in Python using FastAPI</a> by <a href="https://twitter.com/PaurakhSharma" class="external-link" target="_blank">Paurakh Sharma Humagain</a>.
* <a href="https://dev.to/cuongld2/build-simple-api-service-with-python-fastapi-part-1-581o" class="external-link" target="_blank">Build simple API service with Python FastAPI — Part 1</a> by <a href="https://dev.to/cuongld2" class="external-link" target="_blank">cuongld2</a>.
* <a href="https://paulsec.github.io/posts/fastapi_plus_zeit_serverless_fu/" class="external-link" target="_blank">FastAPI + Zeit.co = 🚀
</a> by <a href="https://twitter.com/PaulWebSec" class="external-link" target="_blank">Paul Sec</a>.
* <a href="https://dev.to/tiangolo/build-a-web-api-from-scratch-with-fastapi-the-workshop-2ehe" class="external-link" target="_blank">Build a web API from scratch with FastAPI - the workshop</a> by <a href="https://twitter.com/tiangolo" class="external-link" target="_blank">Sebastián Ramírez (tiangolo)</a>.
* <a href="https://www.twilio.com/blog/build-secure-twilio-webhook-python-fastapi" class="external-link" target="_blank">Build a Secure Twilio Webhook with Python and FastAPI</a> by <a href="https://www.twilio.com" class="external-link" target="_blank">Twilio</a>.
* <a href="https://www.stavros.io/posts/fastapi-with-django/" class="external-link" target="_blank">Using FastAPI with Django</a> by <a href="https://twitter.com/Stavros" class="external-link" target="_blank">Stavros Korokithakis</a>.
* <a href="https://netflixtechblog.com/introducing-dispatch-da4b8a2a8072" class="external-link" target="_blank">Introducing Dispatch</a> by <a href="https://netflixtechblog.com/" class="external-link" target="_blank">Netflix</a>.
### Japanese
* <a href="https://qiita.com/mtitg/items/47770e9a562dd150631d" class="external-link" target="_blank">FastAPIDB接続してCRUDするPython製APIサーバーを構築</a> by <a href="https://qiita.com/mtitg" class="external-link" target="_blank">@mtitg</a>.
@@ -110,11 +127,16 @@ Here's an incomplete list of some of them.
## Podcasts
* <a href="https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855" class="external-link" target="_blank">FastAPI on PythonBytes</a> by <a href="https://pythonbytes.fm/" class="external-link" target="_blank">Python Bytes FM</a>.
* <a href="https://www.pythonpodcast.com/fastapi-web-application-framework-episode-259/" class="external-link" target="_blank">Build The Next Generation Of Python Web Applications With FastAPI - Episode 259 - interview to Sebastían Ramírez (tiangolo)</a> by <a href="https://www.pythonpodcast.com/" class="external-link" target="_blank">Podcast.`__init__`</a>.
## Talks
* <a href="https://www.youtube.com/watch?v=3DLwPcrE5mA" class="external-link" target="_blank">PyCon UK 2019: FastAPI from the ground up</a> by <a href="https://twitter.com/chriswithers13" class="external-link" target="_blank">Chris Withers</a>.
* <a href="https://www.youtube.com/watch?v=z9K5pwb0rt8" class="external-link" target="_blank">PyConBY 2020: Serve ML models easily with FastAPI</a> by <a href="https://twitter.com/tiangolo" class="external-link" target="_blank">Sebastián Ramírez (tiangolo)</a>.
* <a href="https://www.youtube.com/watch?v=PnpTY1f4k2U" class="external-link" target="_blank">[VIRTUAL] Py.Amsterdam's flying Software Circus: Intro to FastAPI</a> by <a href="https://twitter.com/tiangolo" class="external-link" target="_blank">Sebastián Ramírez (tiangolo)</a>.
## Projects
Latest GitHub projects with the topic `fastapi`:

View File

@@ -12,28 +12,18 @@ And there are several ways to get help too.
## Star **FastAPI** in GitHub
You can "star" FastAPI in GitHub (clicking the star button at the top right): <a href="https://github.com/tiangolo/fastapi" class="external-link" target="_blank">https://github.com/tiangolo/fastapi</a>.
You can "star" FastAPI in GitHub (clicking the star button at the top right): <a href="https://github.com/tiangolo/fastapi" class="external-link" target="_blank">https://github.com/tiangolo/fastapi</a>. ⭐️
By adding a star, other users will be able to find it more easily and see that it has been already useful for others.
## Watch the GitHub repository for releases
You can "watch" FastAPI in GitHub (clicking the "watch" button at the top right): <a href="https://github.com/tiangolo/fastapi" class="external-link" target="_blank">https://github.com/tiangolo/fastapi</a>.
You can "watch" FastAPI in GitHub (clicking the "watch" button at the top right): <a href="https://github.com/tiangolo/fastapi" class="external-link" target="_blank">https://github.com/tiangolo/fastapi</a>. 👀
There you can select "Releases only".
Doing it, you will receive notifications (in your email) whenever there's a new release (a new version) of **FastAPI** with bug fixes and new features.
## Join the chat
<a href="https://gitter.im/tiangolo/fastapi?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge" target="_blank">
<img src="https://badges.gitter.im/tiangolo/fastapi.svg" alt="Join the chat at https://gitter.im/tiangolo/fastapi">
</a>
Join the chat on Gitter: <a href="https://gitter.im/tiangolo/fastapi" class="external-link" target="_blank">https://gitter.im/tiangolo/fastapi</a>.
There you can ask quick questions, help others, share ideas, etc.
## Connect with the author
You can connect with <a href="https://tiangolo.com" class="external-link" target="_blank">me (Sebastián Ramírez / `tiangolo`)</a>, the author.
@@ -45,39 +35,32 @@ You can:
* Follow me to see when I create a new Open Source project.
* <a href="https://twitter.com/tiangolo" class="external-link" target="_blank">Follow me on **Twitter**</a>.
* Tell me how you use FastAPI (I love to hear that).
* Ask questions.
* Hear when I make announcements or release new tools.
* <a href="https://www.linkedin.com/in/tiangolo/" class="external-link" target="_blank">Connect with me on **Linkedin**</a>.
* Talk to me.
* Endorse me or recommend me :)
* <a href="https://medium.com/@tiangolo" class="external-link" target="_blank">Read what I write (or follow me) on **Medium**</a>.
* Read other ideas, articles and tools I have created.
* Follow me to see when I publish something new.
* Hear when I make announcements or release new tools (although I use Twitter more often 🤷‍♂).
* Read what I write (or follow me) on <a href="https://dev.to/tiangolo" class="external-link" target="_blank">**Dev.to**</a> or <a href="https://medium.com/@tiangolo" class="external-link" target="_blank">**Medium**</a>.
* Read other ideas, articles, and about tools I have created.
* Follow me to read when I publish something new.
## Tweet about **FastAPI**
<a href="https://twitter.com/compose/tweet?text=I'm loving FastAPI because... https://github.com/tiangolo/fastapi cc @tiangolo" class="external-link" target="_blank">Tweet about **FastAPI**</a> and let me and others know why you like it.
## Let me know how are you using **FastAPI**
<a href="https://twitter.com/compose/tweet?text=I'm loving FastAPI because... https://github.com/tiangolo/fastapi cc @tiangolo" class="external-link" target="_blank">Tweet about **FastAPI**</a> and let me and others know why you like it. 🎉
I love to hear about how **FastAPI** is being used, what have you liked in it, in which project/company are you using it, etc.
You can let me know:
* <a href="https://twitter.com/compose/tweet?text=Hey @tiangolo, I'm using FastAPI at..." class="external-link" target="_blank">On **Twitter**</a>.
* <a href="https://www.linkedin.com/in/tiangolo/" class="external-link" target="_blank">On **Linkedin**</a>.
* <a href="https://medium.com/@tiangolo" class="external-link" target="_blank">On **Medium**</a>.
## Vote for FastAPI
* <a href="https://www.slant.co/options/34241/~fastapi-review" class="external-link" target="_blank">Vote for **FastAPI** in Slant</a>.
* <a href="https://alternativeto.net/software/fastapi/" class="external-link" target="_blank">Vote for **FastAPI** in AlternativeTo</a>.
* <a href="https://github.com/marmelab/awesome-rest/pull/93" class="external-link" target="_blank">Vote for **FastAPI** on awesome-rest</a>.
## Help others with issues in GitHub
You can see <a href="https://github.com/tiangolo/fastapi/issues" class="external-link" target="_blank">existing issues</a> and try and help others.
You can see <a href="https://github.com/tiangolo/fastapi/issues" class="external-link" target="_blank">existing issues</a> and try and help others, most of the times they are questions that you might already know the answer for. 🤓
## Watch the GitHub repository
You can "watch" FastAPI in GitHub (clicking the "watch" button at the top right): <a href="https://github.com/tiangolo/fastapi" class="external-link" target="_blank">https://github.com/tiangolo/fastapi</a>.
You can "watch" FastAPI in GitHub (clicking the "watch" button at the top right): <a href="https://github.com/tiangolo/fastapi" class="external-link" target="_blank">https://github.com/tiangolo/fastapi</a>. 👀
If you select "Watching" instead of "Releases only", you will receive notifications when someone creates a new issue.
@@ -87,9 +70,10 @@ Then you can try and help them solving those issues.
You can <a href="https://github.com/tiangolo/fastapi/issues/new/choose" class="external-link" target="_blank">create a new issue</a> in the GitHub repository, for example to:
* Report a bug/issue.
* Ask a question or ask about a problem.
* Suggest a new feature.
* Ask a question.
**Note**: if you create an issue then I'm going to ask you to also help others. 😉
## Create a Pull Request
@@ -100,12 +84,39 @@ You can <a href="https://github.com/tiangolo/fastapi" class="external-link" targ
* To fix an existing issue/bug.
* To add a new feature.
## Join the chat
<a href="https://gitter.im/tiangolo/fastapi?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge" target="_blank">
<img src="https://badges.gitter.im/tiangolo/fastapi.svg" alt="Join the chat at https://gitter.im/tiangolo/fastapi">
</a>
Join the chat on Gitter: <a href="https://gitter.im/tiangolo/fastapi" class="external-link" target="_blank">https://gitter.im/tiangolo/fastapi</a>.
There you can have quick conversations with others, help others, share ideas, etc.
But have in mind that as it allows more "free conversation", it's easy to ask questions that are too general and more difficult to answer, so, you might not receive answers.
In GitHub issues the template will guide to to write the right question so that you can more easily get a good answer, or even solve the problem yourself even before asking. And in GitHub I can make sure I always answer everything, even if it takes some time. I can't personally do that with the Gitter chat. 😅
Conversations in Gitter are also not as easily searchable as in GitHub, so questions and answers might get lost in the conversation.
On the other side, there's more than 1000 people in the chat, so there's a high chance you'll find someone to talk to there, almost all the time. 😄
## Sponsor the author
You can also financially support the author (me) through <a href="https://github.com/sponsors/tiangolo" class="external-link" target="_blank">GitHub sponsors</a>.
There you could buy me a coffee ☕️ to say thanks 😄.
## Sponsor the tools that power FastAPI
As you have seen in the documentation, FastAPI stands on the shoulders of giants, Starlette and Pydantic.
You can also sponsor:
* <a href="https://github.com/sponsors/samuelcolvin" class="external-link" target="_blank">Samuel Colvin (Pydantic)</a>
* <a href="https://github.com/sponsors/encode" class="external-link" target="_blank">Encode (Starlette, Uvicorn)</a>
---
Thanks!
Thanks! 🚀

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -45,38 +45,44 @@ The key features are:
## Opinions
"*[...] I'm using **FastAPI** a ton these days. [...] I'm actually planning to use it for all of my team's **ML services at Microsoft**. Some of them are getting integrated into the core **Windows** product and some **Office** products.*"
"_[...] I'm using **FastAPI** a ton these days. [...] I'm actually planning to use it for all of my team's **ML services at Microsoft**. Some of them are getting integrated into the core **Windows** product and some **Office** products._"
<div style="text-align: right; margin-right: 10%;">Kabir Khan - <strong>Microsoft</strong> <a href="https://github.com/tiangolo/fastapi/pull/26" target="_blank"><small>(ref)</small></a></div>
---
"*Im over the moon excited about **FastAPI**. Its so fun!*"
"_We adopted the **FastAPI** library to spawn a **REST** server that can be queried to obtain **predictions**. [for Ludwig]_"
<div style="text-align: right; margin-right: 10%;">Piero Molino, Yaroslav Dudin, and Sai Sumanth Miryala - <strong>Uber</strong> <a href="https://eng.uber.com/ludwig-v0-2/" target="_blank"><small>(ref)</small></a></div>
---
"_**Netflix** is pleased to announce the open-source release of our **crisis management** orchestration framework: **Dispatch**! [built with **FastAPI**]_"
<div style="text-align: right; margin-right: 10%;">Kevin Glisson, Marc Vilanova, Forest Monsen - <strong>Netflix</strong> <a href="https://netflixtechblog.com/introducing-dispatch-da4b8a2a8072" target="_blank"><small>(ref)</small></a></div>
---
"_Im over the moon excited about **FastAPI**. Its so fun!_"
<div style="text-align: right; margin-right: 10%;">Brian Okken - <strong><a href="https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855" target="_blank">Python Bytes</a> podcast host</strong> <a href="https://twitter.com/brianokken/status/1112220079972728832" target="_blank"><small>(ref)</small></a></div>
---
"*Honestly, what you've built looks super solid and polished. In many ways, it's what I wanted **Hug** to be - it's really inspiring to see someone build that.*"
"_Honestly, what you've built looks super solid and polished. In many ways, it's what I wanted **Hug** to be - it's really inspiring to see someone build that._"
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong><a href="http://www.hug.rest/" target="_blank">Hug</a> creator</strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div>
---
"*If you're looking to learn one **modern framework** for building REST APIs, check out **FastAPI** [...] It's fast, easy to use and easy to learn [...]*"
"_If you're looking to learn one **modern framework** for building REST APIs, check out **FastAPI** [...] It's fast, easy to use and easy to learn [...]_"
"*We've switched over to **FastAPI** for our **APIs** [...] I think you'll like it [...]*"
"_We've switched over to **FastAPI** for our **APIs** [...] I think you'll like it [...]_"
<div style="text-align: right; margin-right: 10%;">Ines Montani - Matthew Honnibal - <strong><a href="https://explosion.ai" target="_blank">Explosion AI</a> founders - <a href="https://spacy.io" target="_blank">spaCy</a> creators</strong> <a href="https://twitter.com/_inesmontani/status/1144173225322143744" target="_blank"><small>(ref)</small></a> - <a href="https://twitter.com/honnibal/status/1144031421859655680" target="_blank"><small>(ref)</small></a></div>
---
"*We adopted the **FastAPI** library to spawn a **REST** server that can be queried to obtain **predictions**. [for Ludwig]*"
<div style="text-align: right; margin-right: 10%;">Piero Molino, Yaroslav Dudin, and Sai Sumanth Miryala - <strong>Uber</strong> <a href="https://eng.uber.com/ludwig-v0-2/" target="_blank"><small>(ref)</small></a></div>
---
## **Typer**, the FastAPI of CLIs
<a href="https://typer.tiangolo.com" target="_blank"><img src="https://typer.tiangolo.com/img/logo-margin/logo-margin-vector.svg" style="width: 20%;"></a>

View File

@@ -2,6 +2,40 @@
## Latest changes
## 0.56.0
* Add support for ASGI `root_path`:
* Use `root_path` internally for mounted applications, so that OpenAPI and the docs UI works automatically without extra configurations and parameters.
* Add new `root_path` parameter for `FastAPI` applications to provide it in cases where it can be set with the command line (e.g. for Uvicorn and Hypercorn, with the parameter `--root-path`).
* Deprecate `openapi_prefix` parameter in favor of the new `root_path` parameter.
* Add new/updated docs for [Sub Applications - Mounts](https://fastapi.tiangolo.com/advanced/sub-applications/), without `openapi_prefix` (as it is now handled automatically).
* Add new/updated docs for [Behind a Proxy](https://fastapi.tiangolo.com/advanced/behind-a-proxy/), including how to setup a local testing proxy with Traefik and using `root_path`.
* Update docs for [Extending OpenAPI](https://fastapi.tiangolo.com/advanced/extending-openapi/) with the new `openapi_prefix` parameter passed (internally generated from `root_path`).
* Original PR [#1199](https://github.com/tiangolo/fastapi/pull/1199) by [@iksteen](https://github.com/iksteen).
* Update new issue templates and docs: [Help FastAPI - Get Help](https://fastapi.tiangolo.com/help-fastapi/). PR [#1531](https://github.com/tiangolo/fastapi/pull/1531).
* Update GitHub action issue-manager. PR [#1520](https://github.com/tiangolo/fastapi/pull/1520).
* Add new links:
* **English articles**:
* [Real-time Notifications with Python and Postgres](https://wuilly.com/2019/10/real-time-notifications-with-python-and-postgres/) by [Guillermo Cruz](https://wuilly.com/).
* [Microservice in Python using FastAPI](https://dev.to/paurakhsharma/microservice-in-python-using-fastapi-24cc) by [Paurakh Sharma Humagain](https://twitter.com/PaurakhSharma).
* [Build simple API service with Python FastAPI — Part 1](https://dev.to/cuongld2/build-simple-api-service-with-python-fastapi-part-1-581o) by [cuongld2](https://dev.to/cuongld2).
* [FastAPI + Zeit.co = 🚀](https://paulsec.github.io/posts/fastapi_plus_zeit_serverless_fu/) by [Paul Sec](https://twitter.com/PaulWebSec).
* [Build a web API from scratch with FastAPI - the workshop](https://dev.to/tiangolo/build-a-web-api-from-scratch-with-fastapi-the-workshop-2ehe) by [Sebastián Ramírez (tiangolo)](https://twitter.com/tiangolo).
* [Build a Secure Twilio Webhook with Python and FastAPI](https://www.twilio.com/blog/build-secure-twilio-webhook-python-fastapi) by [Twilio](https://www.twilio.com).
* [Using FastAPI with Django](https://www.stavros.io/posts/fastapi-with-django/) by [Stavros Korokithakis](https://twitter.com/Stavros).
* [Introducing Dispatch](https://netflixtechblog.com/introducing-dispatch-da4b8a2a8072) by [Netflix](https://netflixtechblog.com/).
* **Podcasts**:
* [Build The Next Generation Of Python Web Applications With FastAPI - Episode 259 - interview to Sebastían Ramírez (tiangolo)](https://www.pythonpodcast.com/fastapi-web-application-framework-episode-259/) by [Podcast.`__init__`](https://www.pythonpodcast.com/).
* **Talks**:
* [PyConBY 2020: Serve ML models easily with FastAPI](https://www.youtube.com/watch?v=z9K5pwb0rt8) by [Sebastián Ramírez (tiangolo)](https://twitter.com/tiangolo).
* [[VIRTUAL] Py.Amsterdam's flying Software Circus: Intro to FastAPI](https://www.youtube.com/watch?v=PnpTY1f4k2U) by [Sebastián Ramírez (tiangolo)](https://twitter.com/tiangolo).
* PR [#1467](https://github.com/tiangolo/fastapi/pull/1467).
* Add translation to Chinese for [Python Types Intro - Python 类型提示简介](https://fastapi.tiangolo.com/zh/python-types/). PR [#1197](https://github.com/tiangolo/fastapi/pull/1197) by [@waynerv](https://github.com/waynerv).
## 0.55.1
* Fix handling of enums with their own schema in path parameters. To support [samuelcolvin/pydantic#1432](https://github.com/samuelcolvin/pydantic/pull/1432) in FastAPI. PR [#1463](https://github.com/tiangolo/fastapi/pull/1463).
## 0.55.0
* Allow enums to allow them to have their own schemas in OpenAPI. To support [samuelcolvin/pydantic#1432](https://github.com/samuelcolvin/pydantic/pull/1432) in FastAPI. PR [#1461](https://github.com/tiangolo/fastapi/pull/1461).

View File

@@ -92,7 +92,8 @@ nav:
- advanced/sql-databases-peewee.md
- advanced/async-sql-databases.md
- advanced/nosql-databases.md
- advanced/sub-applications-proxy.md
- advanced/sub-applications.md
- advanced/behind-a-proxy.md
- advanced/templates.md
- advanced/graphql.md
- advanced/websockets.md

View File

@@ -44,38 +44,44 @@ Sus características principales son:
## Opiniones
"*[...] I'm using **FastAPI** a ton these days. [...] I'm actually planning to use it for all of my team's **ML services at Microsoft**. Some of them are getting integrated into the core **Windows** product and some **Office** products.*"
"_[...] I'm using **FastAPI** a ton these days. [...] I'm actually planning to use it for all of my team's **ML services at Microsoft**. Some of them are getting integrated into the core **Windows** product and some **Office** products._"
<div style="text-align: right; margin-right: 10%;">Kabir Khan - <strong>Microsoft</strong> <a href="https://github.com/tiangolo/fastapi/pull/26" target="_blank"><small>(ref)</small></a></div>
---
"*Im over the moon excited about **FastAPI**. Its so fun!*"
"_We adopted the **FastAPI** library to spawn a **REST** server that can be queried to obtain **predictions**. [for Ludwig]_"
<div style="text-align: right; margin-right: 10%;">Piero Molino, Yaroslav Dudin, and Sai Sumanth Miryala - <strong>Uber</strong> <a href="https://eng.uber.com/ludwig-v0-2/" target="_blank"><small>(ref)</small></a></div>
---
"_**Netflix** is pleased to announce the open-source release of our **crisis management** orchestration framework: **Dispatch**! [built with **FastAPI**]_"
<div style="text-align: right; margin-right: 10%;">Kevin Glisson, Marc Vilanova, Forest Monsen - <strong>Netflix</strong> <a href="https://netflixtechblog.com/introducing-dispatch-da4b8a2a8072" target="_blank"><small>(ref)</small></a></div>
---
"_Im over the moon excited about **FastAPI**. Its so fun!_"
<div style="text-align: right; margin-right: 10%;">Brian Okken - <strong><a href="https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855" target="_blank">Python Bytes</a> podcast host</strong> <a href="https://twitter.com/brianokken/status/1112220079972728832" target="_blank"><small>(ref)</small></a></div>
---
"*Honestly, what you've built looks super solid and polished. In many ways, it's what I wanted **Hug** to be - it's really inspiring to see someone build that.*"
"_Honestly, what you've built looks super solid and polished. In many ways, it's what I wanted **Hug** to be - it's really inspiring to see someone build that._"
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong><a href="http://www.hug.rest/" target="_blank">Hug</a> creator</strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div>
---
"*If you're looking to learn one **modern framework** for building REST APIs, check out **FastAPI** [...] It's fast, easy to use and easy to learn [...]*"
"_If you're looking to learn one **modern framework** for building REST APIs, check out **FastAPI** [...] It's fast, easy to use and easy to learn [...]_"
"*We've switched over to **FastAPI** for our **APIs** [...] I think you'll like it [...]*"
"_We've switched over to **FastAPI** for our **APIs** [...] I think you'll like it [...]_"
<div style="text-align: right; margin-right: 10%;">Ines Montani - Matthew Honnibal - <strong><a href="https://explosion.ai" target="_blank">Explosion AI</a> founders - <a href="https://spacy.io" target="_blank">spaCy</a> creators</strong> <a href="https://twitter.com/_inesmontani/status/1144173225322143744" target="_blank"><small>(ref)</small></a> - <a href="https://twitter.com/honnibal/status/1144031421859655680" target="_blank"><small>(ref)</small></a></div>
---
"*We adopted the **FastAPI** library to spawn a **REST** server that can be queried to obtain **predictions**. [for Ludwig]*"
<div style="text-align: right; margin-right: 10%;">Piero Molino, Yaroslav Dudin, and Sai Sumanth Miryala - <strong>Uber</strong> <a href="https://eng.uber.com/ludwig-v0-2/" target="_blank"><small>(ref)</small></a></div>
---
## **Typer**, el FastAPI de las CLIs
<a href="https://typer.tiangolo.com" target="_blank"><img src="https://typer.tiangolo.com/img/logo-margin/logo-margin-vector.svg" style="width: 20%;"></a>

View File

@@ -0,0 +1,286 @@
# Python 类型提示简介
**Python 3.6+ 版本**加入了对"类型提示"的支持。
这些**"类型提示"**是一种新的语法(在 Python 3.6 版本加入)用来声明一个变量的<abbr title="例如str、int、float、bool">类型</abbr>。
通过声明变量的类型,编辑器和一些工具能给你提供更好的支持。
这只是一个关于 Python 类型提示的**快速入门 / 复习**。它仅涵盖与 **FastAPI** 一起使用所需的最少部分...实际上只有很少一点。
整个 **FastAPI** 都基于这些类型提示构建,它们带来了许多优点和好处。
但即使你不会用到 **FastAPI**,了解一下类型提示也会让你从中受益。
!!! note
如果你已经精通 Python并且了解关于类型提示的一切知识直接跳到下一章节吧。
## 动机
让我们从一个简单的例子开始:
```Python
{!../../../docs_src/python_types/tutorial001.py!}
```
运行这段程序将输出:
```
John Doe
```
这个函数做了下面这些事情:
* 接收 `first_name``last_name` 参数。
* 通过 `title()` 将每个参数的第一个字母转换为大写形式。
* 中间用一个空格来<abbr title="将它们按顺序放置组合成一个整体。">拼接</abbr>它们。
```Python hl_lines="2"
{!../../../docs_src/python_types/tutorial001.py!}
```
### 修改示例
这是一个非常简单的程序。
现在假设你将从头开始编写这段程序。
在某一时刻,你开始定义函数,并且准备好了参数...。
现在你需要调用一个"将第一个字母转换为大写形式的方法"。
等等,那个方法是什么来着?`upper`?还是 `uppercase``first_uppercase``capitalize`
然后你尝试向程序员老手的朋友——编辑器自动补全寻求帮助。
输入函数的第一个参数 `first_name`,输入点号(`.`)然后敲下 `Ctrl+Space` 来触发代码补全。
但遗憾的是并没有起什么作用:
<img src="https://fastapi.tiangolo.com/img/python-types/image01.png">
### 添加类型
让我们来修改上面例子的一行代码。
我们将把下面这段代码中的函数参数从:
```Python
first_name, last_name
```
改成:
```Python
first_name: str, last_name: str
```
就是这样。
这些就是"类型提示"
```Python hl_lines="1"
{!../../../docs_src/python_types/tutorial002.py!}
```
这和声明默认值是不同的,例如:
```Python
first_name="john", last_name="doe"
```
这两者不一样。
我们用的是冒号(`:`),不是等号(`=`)。
而且添加类型提示一般不会改变原来的运行结果。
现在假设我们又一次正在创建这个函数,这次添加了类型提示。
在同样的地方,通过 `Ctrl+Space` 触发自动补全,你会发现:
<img src="https://fastapi.tiangolo.com/img/python-types/image02.png">
这样,你可以滚动查看选项,直到你找到看起来眼熟的那个:
<img src="https://fastapi.tiangolo.com/img/python-types/image03.png">
## 更多动机
下面是一个已经有类型提示的函数:
```Python hl_lines="1"
{!../../../docs_src/python_types/tutorial003.py!}
```
因为编辑器已经知道了这些变量的类型,所以不仅能对代码进行补全,还能检查其中的错误:
<img src="https://fastapi.tiangolo.com/img/python-types/image04.png">
现在你知道了必须先修复这个问题,通过 `str(age)` 把 `age` 转换成字符串:
```Python hl_lines="2"
{!../../../docs_src/python_types/tutorial004.py!}
```
## 声明类型
你刚刚看到的就是声明类型提示的主要场景。用于函数的参数。
这也是你将在 **FastAPI** 中使用它们的主要场景。
### 简单类型
不只是 `str`,你能够声明所有的标准 Python 类型。
比如以下类型:
* `int`
* `float`
* `bool`
* `bytes`
```Python hl_lines="1"
{!../../../docs_src/python_types/tutorial005.py!}
```
### 嵌套类型
有些容器数据结构可以包含其他的值,比如 `dict`、`list`、`set` 和 `tuple`。它们内部的值也会拥有自己的类型。
你可以使用 Python 的 `typing` 标准库来声明这些类型以及子类型。
它专门用来支持这些类型提示。
#### 列表
例如,让我们来定义一个由 `str` 组成的 `list` 变量。
从 `typing` 模块导入 `List`(注意是大写的 `L`
```Python hl_lines="1"
{!../../../docs_src/python_types/tutorial006.py!}
```
同样以冒号(`:`)来声明这个变量。
输入 `List` 作为类型。
由于列表是带有"子类型"的类型,所以我们把子类型放在方括号中:
```Python hl_lines="4"
{!../../../docs_src/python_types/tutorial006.py!}
```
这表示:"变量 `items` 是一个 `list`,并且这个列表里的每一个元素都是 `str`"。
这样,即使在处理列表中的元素时,你的编辑器也可以提供支持。
没有类型,几乎是不可能实现下面这样:
<img src="https://fastapi.tiangolo.com/img/python-types/image05.png">
注意,变量 `item` 是列表 `items` 中的元素之一。
而且,编辑器仍然知道它是一个 `str`,并为此提供了支持。
#### 元组和集合
声明 `tuple` 和 `set` 的方法也是一样的:
```Python hl_lines="1 4"
{!../../../docs_src/python_types/tutorial007.py!}
```
这表示:
* 变量 `items_t` 是一个 `tuple`,其中的每个元素都是 `int` 类型。
* 变量 `items_s` 是一个 `set`,其中的每个元素都是 `bytes` 类型。
#### 字典
定义 `dict` 时,需要传入两个子类型,用逗号进行分隔。
第一个子类型声明 `dict` 的所有键。
第二个子类型声明 `dict` 的所有值:
```Python hl_lines="1 4"
{!../../../docs_src/python_types/tutorial008.py!}
```
这表示:
* 变量 `prices` 是一个 `dict`
* 这个 `dict` 的所有键为 `str` 类型(可以看作是字典内每个元素的名称)。
* 这个 `dict` 的所有值为 `float` 类型(可以看作是字典内每个元素的价格)。
### 类作为类型
你也可以将类声明为变量的类型。
假设你有一个名为 `Person` 的类,拥有 name 属性:
```Python hl_lines="1 2 3"
{!../../../docs_src/python_types/tutorial009.py!}
```
接下来,你可以将一个变量声明为 `Person` 类型:
```Python hl_lines="6"
{!../../../docs_src/python_types/tutorial009.py!}
```
然后,你将再次获得所有的编辑器支持:
<img src="https://fastapi.tiangolo.com/img/python-types/image06.png">
## Pydantic 模型
<a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a> 是一个用来用来执行数据校验的 Python 库。
你可以将数据的"结构"声明为具有属性的类。
每个属性都拥有类型。
接着你用一些值来创建这个类的实例,这些值会被校验,并被转换为适当的类型(在需要的情况下),返回一个包含所有数据的对象。
然后,你将获得这个对象的所有编辑器支持。
下面的例子来自 Pydantic 官方文档:
```Python
{!../../../docs_src/python_types/tutorial010.py!}
```
!!! info
想进一步了解 <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic请阅读其文档</a>.
整个 **FastAPI** 建立在 Pydantic 的基础之上。
实际上你将在 [教程 - 用户指南](tutorial/index.md){.internal-link target=_blank} 看到很多这种情况。
## **FastAPI** 中的类型提示
**FastAPI** 利用这些类型提示来做下面几件事。
使用 **FastAPI** 时用类型提示声明参数可以获得:
* **编辑器支持**。
* **类型检查**。
...并且 **FastAPI** 还会用这些类型声明来:
* **定义参数要求**:声明对请求路径参数、查询参数、请求头、请求体、依赖等的要求。
* **转换数据**:将来自请求的数据转换为需要的类型。
* **校验数据** 对于每一个请求:
* 当数据校验失败时自动生成**错误信息**返回给客户端。
* 使用 OpenAPI **记录** API
* 然后用于自动生成交互式文档的用户界面。
听上去有点抽象。不过不用担心。你将在 [教程 - 用户指南](tutorial/index.md){.internal-link target=_blank} 中看到所有的实战。
最重要的是,通过使用标准的 Python 类型,只需要在一个地方声明(而不是添加更多的类、装饰器等),**FastAPI** 会为你完成很多的工作。
!!! info
如果你已经阅读了所有教程,回过头来想了解有关类型的更多信息,<a href="https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html" class="external-link" target="_blank">来自 `mypy` 的"速查表"</a>是不错的资源。

View File

@@ -25,6 +25,7 @@ nav:
- pt: /pt/
- zh: /zh/
- features.md
- python-types.md
- 教程 - 用户指南:
- tutorial/index.md
- deployment.md

View File

@@ -0,0 +1,8 @@
from fastapi import FastAPI, Request
app = FastAPI()
@app.get("/app")
def read_main(request: Request):
return {"message": "Hello World", "root_path": request.scope.get("root_path")}

View File

@@ -0,0 +1,8 @@
from fastapi import FastAPI, Request
app = FastAPI(root_path="/api/v1")
@app.get("/app")
def read_main(request: Request):
return {"message": "Hello World", "root_path": request.scope.get("root_path")}

View File

@@ -9,7 +9,7 @@ async def read_items():
return [{"name": "Foo"}]
def custom_openapi():
def custom_openapi(openapi_prefix: str):
if app.openapi_schema:
return app.openapi_schema
openapi_schema = get_openapi(
@@ -17,6 +17,7 @@ def custom_openapi():
version="2.5.0",
description="This is a very custom OpenAPI schema",
routes=app.routes,
openapi_prefix=openapi_prefix,
)
openapi_schema["info"]["x-logo"] = {
"url": "https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png"

View File

@@ -8,7 +8,7 @@ def read_main():
return {"message": "Hello World from main app"}
subapi = FastAPI(openapi_prefix="/subapi")
subapi = FastAPI()
@subapi.get("/sub")

View File

@@ -1,6 +1,6 @@
"""FastAPI framework, high performance, easy to learn, fast to code, ready for production"""
__version__ = "0.55.0"
__version__ = "0.56.0"
from starlette import status

View File

@@ -8,6 +8,7 @@ from fastapi.exception_handlers import (
request_validation_exception_handler,
)
from fastapi.exceptions import RequestValidationError
from fastapi.logger import logger
from fastapi.openapi.docs import (
get_redoc_html,
get_swagger_ui_html,
@@ -36,7 +37,6 @@ class FastAPI(Starlette):
description: str = "",
version: str = "0.1.0",
openapi_url: Optional[str] = "/openapi.json",
openapi_prefix: str = "",
default_response_class: Type[Response] = JSONResponse,
docs_url: Optional[str] = "/docs",
redoc_url: Optional[str] = "/redoc",
@@ -46,6 +46,8 @@ class FastAPI(Starlette):
exception_handlers: Dict[Union[int, Type[Exception]], Callable] = None,
on_startup: Sequence[Callable] = None,
on_shutdown: Sequence[Callable] = None,
openapi_prefix: str = "",
root_path: str = "",
**extra: Dict[str, Any],
) -> None:
self.default_response_class = default_response_class
@@ -68,7 +70,15 @@ class FastAPI(Starlette):
self.description = description
self.version = version
self.openapi_url = openapi_url
self.openapi_prefix = openapi_prefix.rstrip("/")
# TODO: remove when discarding the openapi_prefix parameter
if openapi_prefix:
logger.warning(
'"openapi_prefix" has been deprecated in favor of "root_path", which '
"follows more closely the ASGI standard, is simpler, and more "
"automatic. Check the docs at "
"https://fastapi.tiangolo.com/advanced/sub-applications-proxy/"
)
self.root_path = root_path or openapi_prefix
self.docs_url = docs_url
self.redoc_url = redoc_url
self.swagger_ui_oauth2_redirect_url = swagger_ui_oauth2_redirect_url
@@ -84,7 +94,7 @@ class FastAPI(Starlette):
self.openapi_schema: Optional[Dict[str, Any]] = None
self.setup()
def openapi(self) -> Dict:
def openapi(self, openapi_prefix: str = "") -> Dict:
if not self.openapi_schema:
self.openapi_schema = get_openapi(
title=self.title,
@@ -92,7 +102,7 @@ class FastAPI(Starlette):
openapi_version=self.openapi_version,
description=self.description,
routes=self.routes,
openapi_prefix=self.openapi_prefix,
openapi_prefix=openapi_prefix,
)
return self.openapi_schema
@@ -100,17 +110,22 @@ class FastAPI(Starlette):
if self.openapi_url:
async def openapi(req: Request) -> JSONResponse:
return JSONResponse(self.openapi())
root_path = req.scope.get("root_path", "").rstrip("/")
return JSONResponse(self.openapi(root_path))
self.add_route(self.openapi_url, openapi, include_in_schema=False)
openapi_url = self.openapi_prefix + self.openapi_url
if self.openapi_url and self.docs_url:
async def swagger_ui_html(req: Request) -> HTMLResponse:
root_path = req.scope.get("root_path", "").rstrip("/")
openapi_url = root_path + self.openapi_url
oauth2_redirect_url = self.swagger_ui_oauth2_redirect_url
if oauth2_redirect_url:
oauth2_redirect_url = root_path + oauth2_redirect_url
return get_swagger_ui_html(
openapi_url=openapi_url,
title=self.title + " - Swagger UI",
oauth2_redirect_url=self.swagger_ui_oauth2_redirect_url,
oauth2_redirect_url=oauth2_redirect_url,
init_oauth=self.swagger_ui_init_oauth,
)
@@ -129,6 +144,8 @@ class FastAPI(Starlette):
if self.openapi_url and self.redoc_url:
async def redoc_html(req: Request) -> HTMLResponse:
root_path = req.scope.get("root_path", "").rstrip("/")
openapi_url = root_path + self.openapi_url
return get_redoc_html(
openapi_url=openapi_url, title=self.title + " - ReDoc"
)
@@ -140,6 +157,8 @@ class FastAPI(Starlette):
)
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
if self.root_path:
scope["root_path"] = self.root_path
if AsyncExitStack:
async with AsyncExitStack() as stack:
scope["fastapi_astack"] = stack

View File

@@ -188,6 +188,16 @@ def get_flat_dependant(
return flat_dependant
def get_flat_params(dependant: Dependant) -> List[ModelField]:
flat_dependant = get_flat_dependant(dependant, skip_repeats=True)
return (
flat_dependant.path_params
+ flat_dependant.query_params
+ flat_dependant.header_params
+ flat_dependant.cookie_params
)
def is_scalar_field(field: ModelField) -> bool:
field_info = get_field_info(field)
if not (

View File

@@ -1,9 +1,10 @@
import http.client
from typing import Any, Dict, List, Optional, Sequence, Tuple, Type, cast
from enum import Enum
from typing import Any, Dict, List, Optional, Sequence, Set, Tuple, Type, Union, cast
from fastapi import routing
from fastapi.dependencies.models import Dependant
from fastapi.dependencies.utils import get_flat_dependant
from fastapi.dependencies.utils import get_flat_dependant, get_flat_params
from fastapi.encoders import jsonable_encoder
from fastapi.openapi.constants import (
METHODS_WITH_BODY,
@@ -15,11 +16,14 @@ from fastapi.params import Body, Param
from fastapi.utils import (
generate_operation_id_for_path,
get_field_info,
get_flat_models_from_routes,
get_model_definitions,
)
from pydantic import BaseModel
from pydantic.schema import field_schema, get_model_name_map
from pydantic.schema import (
field_schema,
get_flat_models_from_fields,
get_model_name_map,
)
from pydantic.utils import lenient_issubclass
from starlette.responses import JSONResponse
from starlette.routing import BaseRoute
@@ -64,16 +68,6 @@ status_code_ranges: Dict[str, str] = {
}
def get_openapi_params(dependant: Dependant) -> List[ModelField]:
flat_dependant = get_flat_dependant(dependant, skip_repeats=True)
return (
flat_dependant.path_params
+ flat_dependant.query_params
+ flat_dependant.header_params
+ flat_dependant.cookie_params
)
def get_openapi_security_definitions(flat_dependant: Dependant) -> Tuple[Dict, List]:
security_definitions = {}
operation_security = []
@@ -90,17 +84,22 @@ def get_openapi_security_definitions(flat_dependant: Dependant) -> Tuple[Dict, L
def get_openapi_operation_parameters(
*,
all_route_params: Sequence[ModelField],
model_name_map: Dict[Union[Type[BaseModel], Type[Enum]], str]
) -> List[Dict[str, Any]]:
parameters = []
for param in all_route_params:
field_info = get_field_info(param)
field_info = cast(Param, field_info)
# ignore mypy error until enum schemas are released
parameter = {
"name": param.alias,
"in": field_info.in_.value,
"required": param.required,
"schema": field_schema(param, model_name_map={})[0],
"schema": field_schema(
param, model_name_map=model_name_map, ref_prefix=REF_PREFIX # type: ignore
)[0],
}
if field_info.description:
parameter["description"] = field_info.description
@@ -111,13 +110,16 @@ def get_openapi_operation_parameters(
def get_openapi_operation_request_body(
*, body_field: Optional[ModelField], model_name_map: Dict[Type[BaseModel], str]
*,
body_field: Optional[ModelField],
model_name_map: Dict[Union[Type[BaseModel], Type[Enum]], str]
) -> Optional[Dict]:
if not body_field:
return None
assert isinstance(body_field, ModelField)
# ignore mypy error until enum schemas are released
body_schema, _, _ = field_schema(
body_field, model_name_map=model_name_map, ref_prefix=REF_PREFIX
body_field, model_name_map=model_name_map, ref_prefix=REF_PREFIX # type: ignore
)
field_info = cast(Body, get_field_info(body_field))
request_media_type = field_info.media_type
@@ -176,8 +178,10 @@ def get_openapi_path(
operation.setdefault("security", []).extend(operation_security)
if security_definitions:
security_schemes.update(security_definitions)
all_route_params = get_openapi_params(route.dependant)
operation_parameters = get_openapi_operation_parameters(all_route_params)
all_route_params = get_flat_params(route.dependant)
operation_parameters = get_openapi_operation_parameters(
all_route_params=all_route_params, model_name_map=model_name_map
)
parameters.extend(operation_parameters)
if parameters:
operation["parameters"] = list(
@@ -270,6 +274,38 @@ def get_openapi_path(
return path, security_schemes, definitions
def get_flat_models_from_routes(
routes: Sequence[BaseRoute],
) -> Set[Union[Type[BaseModel], Type[Enum]]]:
body_fields_from_routes: List[ModelField] = []
responses_from_routes: List[ModelField] = []
request_fields_from_routes: List[ModelField] = []
callback_flat_models: Set[Union[Type[BaseModel], Type[Enum]]] = set()
for route in routes:
if getattr(route, "include_in_schema", None) and isinstance(
route, routing.APIRoute
):
if route.body_field:
assert isinstance(
route.body_field, ModelField
), "A request body must be a Pydantic Field"
body_fields_from_routes.append(route.body_field)
if route.response_field:
responses_from_routes.append(route.response_field)
if route.response_fields:
responses_from_routes.extend(route.response_fields.values())
if route.callbacks:
callback_flat_models |= get_flat_models_from_routes(route.callbacks)
params = get_flat_params(route.dependant)
request_fields_from_routes.extend(params)
flat_models = callback_flat_models | get_flat_models_from_fields(
body_fields_from_routes + responses_from_routes + request_fields_from_routes,
known_models=set(),
)
return flat_models
def get_openapi(
*,
title: str,
@@ -286,9 +322,11 @@ def get_openapi(
components: Dict[str, Dict] = {}
paths: Dict[str, Dict] = {}
flat_models = get_flat_models_from_routes(routes)
model_name_map = get_model_name_map(flat_models)
# ignore mypy error until enum schemas are released
model_name_map = get_model_name_map(flat_models) # type: ignore
# ignore mypy error until enum schemas are released
definitions = get_model_definitions(
flat_models=flat_models, model_name_map=model_name_map
flat_models=flat_models, model_name_map=model_name_map # type: ignore
)
for route in routes:
if isinstance(route, routing.APIRoute):

View File

@@ -1,17 +1,16 @@
import functools
import re
from dataclasses import is_dataclass
from typing import Any, Dict, List, Optional, Sequence, Set, Type, Union, cast
from enum import Enum
from typing import Any, Dict, Optional, Set, Type, Union, cast
import fastapi
from fastapi import routing
from fastapi.logger import logger
from fastapi.openapi.constants import REF_PREFIX
from pydantic import BaseConfig, BaseModel, create_model
from pydantic.class_validators import Validator
from pydantic.schema import get_flat_models_from_fields, model_process_schema
from pydantic.schema import model_process_schema
from pydantic.utils import lenient_issubclass
from starlette.routing import BaseRoute
try:
from pydantic.fields import FieldInfo, ModelField, UndefinedType
@@ -50,38 +49,16 @@ def warning_response_model_skip_defaults_deprecated() -> None:
)
def get_flat_models_from_routes(routes: Sequence[BaseRoute]) -> Set[Type[BaseModel]]:
body_fields_from_routes: List[ModelField] = []
responses_from_routes: List[ModelField] = []
callback_flat_models: Set[Type[BaseModel]] = set()
for route in routes:
if getattr(route, "include_in_schema", None) and isinstance(
route, routing.APIRoute
):
if route.body_field:
assert isinstance(
route.body_field, ModelField
), "A request body must be a Pydantic Field"
body_fields_from_routes.append(route.body_field)
if route.response_field:
responses_from_routes.append(route.response_field)
if route.response_fields:
responses_from_routes.extend(route.response_fields.values())
if route.callbacks:
callback_flat_models |= get_flat_models_from_routes(route.callbacks)
flat_models = callback_flat_models | get_flat_models_from_fields(
body_fields_from_routes + responses_from_routes, known_models=set()
)
return flat_models
def get_model_definitions(
*, flat_models: Set[Type[BaseModel]], model_name_map: Dict[Type[BaseModel], str]
*,
flat_models: Set[Union[Type[BaseModel], Type[Enum]]],
model_name_map: Dict[Union[Type[BaseModel], Type[Enum]], str],
) -> Dict[str, Any]:
definitions: Dict[str, Dict] = {}
for model in flat_models:
# ignore mypy error until enum schemas are released
m_schema, m_definitions, m_nested_models = model_process_schema(
model, model_name_map=model_name_map, ref_prefix=REF_PREFIX
model, model_name_map=model_name_map, ref_prefix=REF_PREFIX # type: ignore
)
definitions.update(m_definitions)
model_name = model_name_map[model]

View File

@@ -3,4 +3,4 @@ set -x
autoflake --remove-all-unused-imports --recursive --remove-unused-variables --in-place docs_src fastapi tests scripts --exclude=__init__.py
black fastapi tests docs_src scripts
isort --multi-line=3 --trailing-comma --force-grid-wrap=0 --combine-as --line-width 88 --recursive --thirdparty fastapi --apply fastapi tests docs_src scripts
isort --multi-line=3 --trailing-comma --force-grid-wrap=0 --combine-as --line-width 88 --recursive --thirdparty fastapi --thirdparty pydantic --thirdparty starlette --apply fastapi tests docs_src scripts

View File

@@ -5,4 +5,4 @@ set -x
mypy fastapi
black fastapi tests --check
isort --multi-line=3 --trailing-comma --force-grid-wrap=0 --combine-as --line-width 88 --recursive --check-only --thirdparty fastapi fastapi tests
isort --multi-line=3 --trailing-comma --force-grid-wrap=0 --combine-as --line-width 88 --recursive --check-only --thirdparty fastapi --thirdparty fastapi --thirdparty pydantic --thirdparty starlette fastapi tests

View File

@@ -0,0 +1,43 @@
from fastapi import FastAPI, Request
from fastapi.testclient import TestClient
app = FastAPI(openapi_prefix="/api/v1")
@app.get("/app")
def read_main(request: Request):
return {"message": "Hello World", "root_path": request.scope.get("root_path")}
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/api/v1/app": {
"get": {
"summary": "Read Main",
"operationId": "read_main_app_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
}
}
},
}
def test_openapi():
response = client.get("/openapi.json")
assert response.status_code == 200
assert response.json() == openapi_schema
def test_main():
response = client.get("/app")
assert response.status_code == 200
assert response.json() == {"message": "Hello World", "root_path": "/api/v1"}

View File

View File

@@ -0,0 +1,36 @@
from fastapi.testclient import TestClient
from behind_a_proxy.tutorial001 import app
client = TestClient(app, root_path="/api/v1")
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/api/v1/app": {
"get": {
"summary": "Read Main",
"operationId": "read_main_app_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
}
}
},
}
def test_openapi():
response = client.get("/openapi.json")
assert response.status_code == 200
assert response.json() == openapi_schema
def test_main():
response = client.get("/app")
assert response.status_code == 200
assert response.json() == {"message": "Hello World", "root_path": "/api/v1"}

View File

@@ -0,0 +1,36 @@
from fastapi.testclient import TestClient
from behind_a_proxy.tutorial002 import app
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/api/v1/app": {
"get": {
"summary": "Read Main",
"operationId": "read_main_app_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
}
}
},
}
def test_openapi():
response = client.get("/openapi.json")
assert response.status_code == 200
assert response.json() == openapi_schema
def test_main():
response = client.get("/app")
assert response.status_code == 200
assert response.json() == {"message": "Hello World", "root_path": "/api/v1"}

View File

@@ -87,7 +87,7 @@ openapi_schema2 = {
"parameters": [
{
"required": True,
"schema": {"$ref": "#/definitions/ModelName"},
"schema": {"$ref": "#/components/schemas/ModelName"},
"name": "model_name",
"in": "path",
}
@@ -124,6 +124,12 @@ openapi_schema2 = {
}
},
},
"ModelName": {
"title": "ModelName",
"enum": ["alexnet", "resnet", "lenet"],
"type": "string",
"description": "An enumeration.",
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],