Giter Club home page Giter Club logo

fastapi's Introduction

FastAPI

FastAPI framework, high performance, easy to learn, fast to code, ready for production

Test Coverage Package version Supported Python versions


Documentation: https://fastapi.tiangolo.com

Source Code: https://github.com/tiangolo/fastapi


FastAPI is a modern, fast (high-performance), web framework for building APIs with Python 3.8+ based on standard Python type hints.

The key features are:

  • Fast: Very high performance, on par with NodeJS and Go (thanks to Starlette and Pydantic). One of the fastest Python frameworks available.
  • Fast to code: Increase the speed to develop features by about 200% to 300%. *
  • Fewer bugs: Reduce about 40% of human (developer) induced errors. *
  • Intuitive: Great editor support. Completion everywhere. Less time debugging.
  • Easy: Designed to be easy to use and learn. Less time reading docs.
  • Short: Minimize code duplication. Multiple features from each parameter declaration. Fewer bugs.
  • Robust: Get production-ready code. With automatic interactive documentation.
  • Standards-based: Based on (and fully compatible with) the open standards for APIs: OpenAPI (previously known as Swagger) and JSON Schema.

* estimation based on tests on an internal development team, building production applications.

Sponsors

Other sponsors

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."

Kabir Khan - Microsoft (ref)

"We adopted the FastAPI library to spawn a REST server that can be queried to obtain predictions. [for Ludwig]"

Piero Molino, Yaroslav Dudin, and Sai Sumanth Miryala - Uber (ref)

"Netflix is pleased to announce the open-source release of our crisis management orchestration framework: Dispatch! [built with FastAPI]"

Kevin Glisson, Marc Vilanova, Forest Monsen - Netflix (ref)

"I’m over the moon excited about FastAPI. It’s so fun!"

Brian Okken - Python Bytes podcast host (ref)

"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."

Timothy Crosley - Hug creator (ref)

"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 [...]"

Ines Montani - Matthew Honnibal - Explosion AI founders - spaCy creators (ref) - (ref)

"If anyone is looking to build a production Python API, I would highly recommend FastAPI. It is beautifully designed, simple to use and highly scalable, it has become a key component in our API first development strategy and is driving many automations and services such as our Virtual TAC Engineer."

Deon Pillsbury - Cisco (ref)

Typer, the FastAPI of CLIs

If you are building a CLI app to be used in the terminal instead of a web API, check out Typer.

Typer is FastAPI's little sibling. And it's intended to be the FastAPI of CLIs. ⌨️ 🚀

Requirements

Python 3.8+

FastAPI stands on the shoulders of giants:

Installation

$ pip install fastapi

---> 100%

You will also need an ASGI server, for production such as Uvicorn or Hypercorn.

$ pip install "uvicorn[standard]"

---> 100%

Example

Create it

  • Create a file main.py with:
from typing import Union

from fastapi import FastAPI

app = FastAPI()


@app.get("/")
def read_root():
    return {"Hello": "World"}


@app.get("/items/{item_id}")
def read_item(item_id: int, q: Union[str, None] = None):
    return {"item_id": item_id, "q": q}
Or use async def...

If your code uses async / await, use async def:

from typing import Union

from fastapi import FastAPI

app = FastAPI()


@app.get("/")
async def read_root():
    return {"Hello": "World"}


@app.get("/items/{item_id}")
async def read_item(item_id: int, q: Union[str, None] = None):
    return {"item_id": item_id, "q": q}

Note:

If you don't know, check the "In a hurry?" section about async and await in the docs.

Run it

Run the server with:

$ uvicorn main:app --reload

INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [28720]
INFO:     Started server process [28722]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
About the command uvicorn main:app --reload...

The command uvicorn main:app refers to:

  • main: the file main.py (the Python "module").
  • app: the object created inside of main.py with the line app = FastAPI().
  • --reload: make the server restart after code changes. Only do this for development.

Check it

Open your browser at http://127.0.0.1:8000/items/5?q=somequery.

You will see the JSON response as:

{"item_id": 5, "q": "somequery"}

You already created an API that:

  • Receives HTTP requests in the paths / and /items/{item_id}.
  • Both paths take GET operations (also known as HTTP methods).
  • The path /items/{item_id} has a path parameter item_id that should be an int.
  • The path /items/{item_id} has an optional str query parameter q.

Interactive API docs

Now go to http://127.0.0.1:8000/docs.

You will see the automatic interactive API documentation (provided by Swagger UI):

Swagger UI

Alternative API docs

And now, go to http://127.0.0.1:8000/redoc.

You will see the alternative automatic documentation (provided by ReDoc):

ReDoc

Example upgrade

Now modify the file main.py to receive a body from a PUT request.

Declare the body using standard Python types, thanks to Pydantic.

from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    price: float
    is_offer: Union[bool, None] = None


@app.get("/")
def read_root():
    return {"Hello": "World"}


@app.get("/items/{item_id}")
def read_item(item_id: int, q: Union[str, None] = None):
    return {"item_id": item_id, "q": q}


@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item):
    return {"item_name": item.name, "item_id": item_id}

The server should reload automatically (because you added --reload to the uvicorn command above).

Interactive API docs upgrade

Now go to http://127.0.0.1:8000/docs.

  • The interactive API documentation will be automatically updated, including the new body:

Swagger UI

  • Click on the button "Try it out", it allows you to fill the parameters and directly interact with the API:

Swagger UI interaction

  • Then click on the "Execute" button, the user interface will communicate with your API, send the parameters, get the results and show them on the screen:

Swagger UI interaction

Alternative API docs upgrade

And now, go to http://127.0.0.1:8000/redoc.

  • The alternative documentation will also reflect the new query parameter and body:

ReDoc

Recap

In summary, you declare once the types of parameters, body, etc. as function parameters.

You do that with standard modern Python types.

You don't have to learn a new syntax, the methods or classes of a specific library, etc.

Just standard Python 3.8+.

For example, for an int:

item_id: int

or for a more complex Item model:

item: Item

...and with that single declaration you get:

  • Editor support, including:
    • Completion.
    • Type checks.
  • Validation of data:
    • Automatic and clear errors when the data is invalid.
    • Validation even for deeply nested JSON objects.
  • Conversion of input data: coming from the network to Python data and types. Reading from:
    • JSON.
    • Path parameters.
    • Query parameters.
    • Cookies.
    • Headers.
    • Forms.
    • Files.
  • Conversion of output data: converting from Python data and types to network data (as JSON):
    • Convert Python types (str, int, float, bool, list, etc).
    • datetime objects.
    • UUID objects.
    • Database models.
    • ...and many more.
  • Automatic interactive API documentation, including 2 alternative user interfaces:
    • Swagger UI.
    • ReDoc.

Coming back to the previous code example, FastAPI will:

  • Validate that there is an item_id in the path for GET and PUT requests.
  • Validate that the item_id is of type int for GET and PUT requests.
    • If it is not, the client will see a useful, clear error.
  • Check if there is an optional query parameter named q (as in http://127.0.0.1:8000/items/foo?q=somequery) for GET requests.
    • As the q parameter is declared with = None, it is optional.
    • Without the None it would be required (as is the body in the case with PUT).
  • For PUT requests to /items/{item_id}, Read the body as JSON:
    • Check that it has a required attribute name that should be a str.
    • Check that it has a required attribute price that has to be a float.
    • Check that it has an optional attribute is_offer, that should be a bool, if present.
    • All this would also work for deeply nested JSON objects.
  • Convert from and to JSON automatically.
  • Document everything with OpenAPI, that can be used by:
    • Interactive documentation systems.
    • Automatic client code generation systems, for many languages.
  • Provide 2 interactive documentation web interfaces directly.

We just scratched the surface, but you already get the idea of how it all works.

Try changing the line with:

    return {"item_name": item.name, "item_id": item_id}

...from:

        ... "item_name": item.name ...

...to:

        ... "item_price": item.price ...

...and see how your editor will auto-complete the attributes and know their types:

editor support

For a more complete example including more features, see the Tutorial - User Guide.

Spoiler alert: the tutorial - user guide includes:

  • Declaration of parameters from other different places as: headers, cookies, form fields and files.
  • How to set validation constraints as maximum_length or regex.
  • A very powerful and easy to use Dependency Injection system.
  • Security and authentication, including support for OAuth2 with JWT tokens and HTTP Basic auth.
  • More advanced (but equally easy) techniques for declaring deeply nested JSON models (thanks to Pydantic).
  • GraphQL integration with Strawberry and other libraries.
  • Many extra features (thanks to Starlette) as:
    • WebSockets
    • extremely easy tests based on HTTPX and pytest
    • CORS
    • Cookie Sessions
    • ...and more.

Performance

Independent TechEmpower benchmarks show FastAPI applications running under Uvicorn as one of the fastest Python frameworks available, only below Starlette and Uvicorn themselves (used internally by FastAPI). (*)

To understand more about it, see the section Benchmarks.

Optional Dependencies

Used by Pydantic:

Used by Starlette:

  • httpx - Required if you want to use the TestClient.
  • jinja2 - Required if you want to use the default template configuration.
  • python-multipart - Required if you want to support form "parsing", with request.form().
  • itsdangerous - Required for SessionMiddleware support.
  • pyyaml - Required for Starlette's SchemaGenerator support (you probably don't need it with FastAPI).
  • ujson - Required if you want to use UJSONResponse.

Used by FastAPI / Starlette:

  • uvicorn - for the server that loads and serves your application.
  • orjson - Required if you want to use ORJSONResponse.

You can install all of these with pip install "fastapi[all]".

License

This project is licensed under the terms of the MIT license.

fastapi's People

Contributors

alejsdev avatar alertred avatar alexandrhub avatar barsi avatar batlopes avatar dependabot[bot] avatar dmontagu avatar euri10 avatar github-actions[bot] avatar hard-coders avatar hasansezertasan avatar jaystone776 avatar kanikim avatar kludex avatar mariacamilagl avatar nilslindemann avatar ninahwang avatar pablocm83 avatar pre-commit-ci[bot] avatar rjnemo avatar runningikkyu avatar samuelcolvin avatar serrones avatar smlep avatar swftalpc avatar tiangolo avatar tokusumi avatar waynerv avatar xewus avatar xzmeng avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fastapi's Issues

Multiple response content types in one endpoint?

Description

How can I [...]?

I'm trying to figure out how to use one endpoint for multiple response content_types? I want to create an /export API endpoint and be able to dump (from memory) responses such as application/json, application/yaml or application/zip based on the content-type requested.

Everything I've seen so far is just for a single response type - though I've probably missed something because this is one of the best documented projects I've used.

Updating pydantic

Is your feature request related to a problem? Please describe.

I need the ability to use the Extra.forbid config option to indicate when I have extra values in my pydantic model. This feature landed in v0.19.0 and was fixed/enhanced in v0.20.0. There appears to be one breaking change in v0.20.0 since v0.18.2 (which is what is currently locked for fastapi)

Describe the solution you'd like
I'd like to update to pydantic v0.20.0. I can generate a PR for this, but I'm not sure why you locked it at v0.18.2

Trying to use Starlette DatabaseMiddleware

Greetings and congrats again for this amazingly well documented library, I'm happy Twitter made me discover this !

So I decided to dive a little bit into it, and I was in particular interested in the Starlette DatabaseMiddleware.

I then cloned the related test in Starlette and reworked it using FastAPI here: https://github.com/euri10/fastapi/blob/dbmiddleware/tests/test_dbmiddleware.py

so in this test we got:

  • one starlette instance of Starlette starlette=Starlette() that makes the very same tests as in the test_database mentionned aboveand

  • one fastapi instance of FastAPI fastapi=FastAPI(), whose routes have been adapted by me to take the new "framework" into account (mainly switching to app.get and app.post instead of app.route, making use of pydantic models etc,....)

Each instance is tested separately in a class, namely class TestStarletteStyle(): and class TestFastAPIStyle():

Obviously TestStarletteStyle passes like in the original file.
Interestingly if you change starlette=Starlette() to starlette=FastAPI() TestStarletteStyle passes also, which makes sense since if I got it correctly a fastapi.post is the same as a fastapi.route, methods=["POST"]

However TestFastAPIStyle fails at various points, all tests fails but not all asserts:

  • fails here as while the previous RuntimeError has been raised, the entry has been put in the database, while in the TestStarletteStyle the RuntimeError prevented this, is seems related to the rollback_on_shutdown=True that would not have been taken into account if i understand correctly, but take this with caution I'm brand new to your code and starlette's one.
  • fails here too, and looking at what we got in db (5 entries instead of 2 in the test) it seems also related to a rollback not made.
  • finally fails here too, and we got 6 entries in db instead of 1 tested, same intuition.

So there's seem to be something incorrect with the rollback setting, happy to dig further but you surely have ideas, if making a PR with my failing branch is ok let me know

Return lists

Hi there!

First of all, gerat project! Thank You for sharing it!

i'm a somewhat a beginner, i'd like some help if you guys don't mind. (I couldn't find another forum for questions)

I've managed to read a list of objects inside a json, by passing the body like this:

[
  {
    "name": "Item name example 1",
    "price": 100,
    "is_offer": true
  },
  {
    "name": "Item name example 2",
    "price": 100,
    "is_offer": true
  },
  {
    "name": "Item name example 3",
    "price": 100,
    "is_offer": true
  }
]

The objective of my micro example is to receive a list of Items (the same base class of the API example), do some processing (change price value by a fixed factor) and return that altered list.

this works:

@app.put("/items/{item_id}")
def create_item(item_id: int, items: List[Item]):    
    return {"total" : len(items)}

and i can iterate trough the list with

for item in items:

but when i try to return the list with

return {items}

i get "TypeError: unhashable type: 'list'" error.

is there a simple way to return the list? Or i'll need to create the return string manually?

Storing object instances in the app context

Description

In Flask, I've found this to be a common pattern to do something like this:

run_app.py

from api.factory import create_app

app = create_app()

api/factory.py

from flask import Flask, current_app
import redis
import config
from api.resources.basic import basic

def create_app():
    """Create the app."""
    app = Flask(__name__)
    app.config.from_object(config.ProductionConfig)
    app.redis = redis.Redis(host=app.config["REDIS_HOST"], port=app.config["REDIS_PORT"])
    app.register_blueprint(basic, url_prefix="/api/v1")

api/resources/basic.py

from flask import current_app
from flask import Blueprint
from webargs import fields
from webargs.flaskparser import use_args

mod = Blueprint('basic', __name__)
write_args = {"value": fields.Str(required=True)}

@mod.route('/<str:key>', methods=['POST'])
@use_args(write_args, key)
def create_resource(args, key):
   """POST resource to redis."""
   current_app.redis.set(key, args["value"])
   current_app.logger.info("inserted value into redis")
   return {key: args["value"]}, 201

@mod.route('/<str:key>', methods=['GET'])
def retrieve_resource(key):
   """GET value from redis."""
   return {key: current_app.redis.get(key)}, 200

Is it possible to have a 'global' app context where you can put objects shared across request contexts, where you would access objects that you don't want to keep reinstantiating because it might be expensive and because of the convenience and how it makes it easier to reason about where 'objects' live. Like current_app.redis, or current_app.logger, or current_app.kafka_producer. I read about Depends, and saw the examples provided for how to deal with dependencies / resources but I didn't see something like the above.

Might be a bad practice, but it's worked well for me so far.

Use a default values for a request Swagger UI documentation

First of all, amazing project @tiangolo !

I have a quick question. I want the request body example in the Swagger UI documentation to represent some API calls the user would want to make. However, all I can see are the default values I have set in the BaseModel like "string" (see image). How do I change the examples in the documentation without changing the BaseModel? E.g. I wan't an user using the API to get another default value than the value you see in the Swagger documentation.

image

Please include a crud generator or a tutorial on doing crud with vue.

Is your feature request related to a problem? Please describe.
In order to rapid development, A crud generator (front end) would be great.

Describe the solution you'd like
A clear and concise description of what you want to happen.

Describe alternatives you've considered
It can be documentation about generating basic crud.

Additional context
I'm not sure wether this is within the framework scope, but I just want to get flask behind.
This, and user/permission management are maybe the only 2 things lacking. IMHO.

jsonable_encoder crash

Describe the bug

The jsonable_encoder crashes rightfully when passing a list of postgres Records, it's certainly a mis usage on my end, but I think that is something that would be interesting maybe to add in that function

Crash log
Connected to pydev debugger (build 183.5429.31)
backend_1_f0c43d42b730 | WARNING:root:email-validator not installed, email fields will be treated as str.
backend_1_f0c43d42b730 | To install, run: pip install email-validator
backend_1_f0c43d42b730 | DEBUG:app.app:postgresql://postgres:postgres@db:5432/fastapi_dev
backend_1_f0c43d42b730 | INFO:uvicorn:Started server process [1]
backend_1_f0c43d42b730 | INFO:uvicorn:Waiting for application startup.
backend_1_f0c43d42b730 | INFO:uvicorn:Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
backend_1_f0c43d42b730 | Traceback (most recent call last):
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 78, in jsonable_encoder
backend_1_f0c43d42b730 |     encoder = ENCODERS_BY_TYPE[type(obj)]
backend_1_f0c43d42b730 | KeyError: <class 'sqlalchemy.sql.base.ColumnCollection'>
backend_1_f0c43d42b730 | 
backend_1_f0c43d42b730 | During handling of the above exception, another exception occurred:
backend_1_f0c43d42b730 | 
backend_1_f0c43d42b730 | Traceback (most recent call last):
backend_1_f0c43d42b730 |   File "/opt/.pycharm_helpers/pydev/_pydevd_bundle/pydevd_trace_dispatch_regular.py", line 145, in __call__
backend_1_f0c43d42b730 |     if not is_stepping and cache_key in cache_skips:
backend_1_f0c43d42b730 | RecursionError: maximum recursion depth exceeded in comparison
backend_1_f0c43d42b730 | Fatal Python error: Cannot recover from stack overflow.
backend_1_f0c43d42b730 | 
backend_1_f0c43d42b730 | Thread 0x00007f5d9ffa9ae8 (most recent call first):
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/threading.py", line 300 in wait
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/threading.py", line 552 in wait
backend_1_f0c43d42b730 |   File "/opt/.pycharm_helpers/pydev/pydevd.py", line 128 in _on_run
backend_1_f0c43d42b730 |   File "/opt/.pycharm_helpers/pydev/_pydevd_bundle/pydevd_comm.py", line 320 in run
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/threading.py", line 917 in _bootstrap_inner
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/threading.py", line 885 in _bootstrap
backend_1_f0c43d42b730 | 
backend_1_f0c43d42b730 | Thread 0x00007f5da00acae8 (most recent call first):
backend_1_f0c43d42b730 |   File "/opt/.pycharm_helpers/pydev/_pydevd_bundle/pydevd_comm.py", line 382 in _on_run
backend_1_f0c43d42b730 |   File "/opt/.pycharm_helpers/pydev/_pydevd_bundle/pydevd_comm.py", line 320 in run
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/threading.py", line 917 in _bootstrap_inner
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/threading.py", line 885 in _bootstrap
backend_1_f0c43d42b730 | 
backend_1_f0c43d42b730 | Thread 0x00007f5da01afae8 (most recent call first):
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/threading.py", line 300 in wait
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/queue.py", line 179 in get
backend_1_f0c43d42b730 |   File "/opt/.pycharm_helpers/pydev/_pydevd_bundle/pydevd_comm.py", line 459 in _on_run
backend_1_f0c43d42b730 |   File "/opt/.pycharm_helpers/pydev/_pydevd_bundle/pydevd_comm.py", line 320 in run
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/threading.py", line 917 in _bootstrap_inner
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/threading.py", line 885 in _bootstrap
backend_1_f0c43d42b730 | 
backend_1_f0c43d42b730 | Current thread 0x00007f5da6097b88 (most recent call first):
backend_1_f0c43d42b730 |   File "/opt/.pycharm_helpers/pydev/_pydevd_bundle/pydevd_trace_dispatch_regular.py", line 145 in __call__
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/sqlalchemy/sql/operators.py", line 425 in __getitem__
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/sqlalchemy/sql/elements.py", line 707 in operate
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/sqlalchemy/sql/operators.py", line 432 in __getitem__
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 83 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 96 in jsonable_encoder
backend_1_f0c43d42b730 |   File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 54 in jsonable_encoder
backend_1_f0c43d42b730 |   ...
fastapi_appfactory_backend_1_f0c43d42b730 exited with code 139
Aborting on container exit...

To Reproduce

Here's my simple app, I want to test the databases package from starlette

import logging
from typing import List

from fastapi import FastAPI

from app import settings
from app.settings import database
from app.models import notes, NoteIn


logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

logger.debug(settings.DATABASE_URL)
app = FastAPI()

app.debug = settings.DEBUG


@app.on_event("startup")
async def startup():
    await database.connect()


@app.on_event("shutdown")
async def shutdown():
    await database.disconnect()


@app.get("/")
async def root():
    logger.debug(database)
    return {"message": "Hello World"}


@app.get("/notes", response_model=List[NoteIn])
async def list_notes():
    query = notes.select()
    results = await database.fetch_all(query)
    # content = [
    #     {"text": result["text"], "completed": result["completed"]} for result in results
    # ]
    # return content
    return results


@app.post("/notes")
async def add_note(note: NoteIn):
    query = notes.insert().values(**note.dict())
    await database.execute(query)
    return note

Should you want I may give you the full gitlab repo address that can reproduce it simply with a docker-compose, instead of copying all files :)

Expected behavior

If I return content instead of results in the list_notes route I got no problem, so that one workaround.
Another workaround is to return
return [r._row for r in results]
but i don't like much using private attributes

I got the feeling that results, being a list of databases.backends.postgres.Record it might be rightfully "parsed" by jsonable_encoder by adding another if isinstance(obj, blablabla): check

So maybe not a bug, but a possible enhancement ;)

when using DatabaseMiddleware, the fetchall method (note, no underscore in it) return a list of asyncpg.Records which the encoder parsed fine: results = await request.database.fetchall(query)

now we got a list of databases.backends.postgres.Record

Environment:

  • OS: debian buster
  • FastAPI Version [e.g. 0.3.0], get it with: 0.5.0
import fastapi
print(fastapi.__version__)
  • Python version, get it with: 3.7.2
python --version

Additional context
Add any other context about the problem here.

Uvicorn 0.5

  • reloading now works programmaticaly. Not just when running uvicorn from the console.
  • Multi-worker support is available. (But not on by default)

Changes you'll want to make:

Switch the docs to uvicorn --reload instead of uvicorn --debug.

Debug tracebacks will be becoming strictly an application-level concern, and will disappear from uvicorn in a future version. If you want debug tracebacks, make sure you're using starlette's ServerErrorMiddleware, and switching it on or off appropriately depending on if FastAPI is running in a debug mode or not.

Clarify run_in_threadpool magic

Good day,

First of all - many thanks for creating this project. Such a relief - you really nailed the proper amount of "batteries included". And docs are great - not a given thing for such a young project.

I'm studying SQL Database example where you note that SQLAlchemy is not compatible with async hence you configure path using def instead of async def. However this example raises and eyebrow because it should block the whole server while database is executing - only that it does not because the magic you do by running request handlers through threadpool unless they are co-routines.

While this magic is nice convenience, don't you think I can confuse users, particularly those who are less experienced with the whole sync/async co-existence semantics in Python world?

My bottom line is that IMHO it worth adding a clear "Unless you define your request handlers as coroutines FastAPI will run them them in separate thread" note in the "SQL Databases" and "Concurrency and async / await" chapters. What do you think?

Me coming from aiohttp is used to have non-async request handlers for simple methods that don't involve any IO and this magical FastAPI behavior, while useful, would've caused a performance penalty on those methods.

Recomended way to manage users?

Description

What is the best way to handle users registers, login, pasword changes, unregister?
A way to manage permissions and roles?
Additional context
Something similar to flask-auth , flask -security, flask login?

Streaming Large Files

I am working with this example in the documentation that shows a basic example of how to work with files.

Is it possible to stream a large file with this? What is the correct pattern?

It's possible to stream files with starlette, so how would integrating this functionality with fastapi work?

Thank you!

How can I return a 202 Accepted response for long running REST call?

Hope this is the right place for a question.

I have a REST API post request that does a lot of computation, /crunch. Rather than block the event loop I would like /crunch to return 202 Accepted status code along with a token string. Then the user of the API can call get request /result/{token} to check on the status of the computation. This is outlined nicely here for example.

Is it possible to modify the response status code, for example, similar to this approach in Sanic?

fastapi issue of swagger display ?

Description

I have a pydantic model with Enum and Union parameters, and while they both appear in Schema, in a post route they are not displayed in the Example edit section.
I hesitated to file a bug but I;d prefer to ask if that's something that's feasible first.

I was surprised and made a small exercice, take this model:

class DisplayMe(BaseModel):
    my_bool: bool
    my_float: float
    my_str: str
    my_int: int
    my_dict: dict
    # my_list: list
    # my_tuple: tuple
    # my_set: set
    # my_List_str: List[str]
    # my_tuple_str_int: Tuple[str, int]
    # my_dict_str_int: Dict[str, int]
    # my_union_str_int: Union[str, int]
    # my_enum: Enum
    my_emailstr: EmailStr
    my_name_email: NameEmail
    my_urlstr: UrlStr
    my_dsn: DSN
    my_bytes: bytes
    my_decimal: Decimal
    my_uuid1: UUID1
    my_uuid3: UUID3
    my_uuid4: UUID4
    my_uuid5: UUID5
    my_uuid: UUID
    my_filepath: FilePath
    my_directorypath: DirectoryPath
    my_path: Path
    my_datetime: datetime
    my_date: date

add this route:

@router.post("/testypes")
async def types(tt: DisplayMe):
    return tt

should you uncomment any of the pydantic types commented in the DisplayMe model above, then the Edit section is empty, there's nothing to edit.

But I can see them in schema, for instance on the below screen my_tuple is visible in the Schema:

Imgur

Now if I put them commented they are in the Edit section:
Imgur

question is : is it possible to have the commented pydantic types in the swagger edit section, I'm mostyl interested in Union and Enum in fact :)

What is best way to override logging to use structlog?

Description

How can I [...]? I'm using structlog for logging in the application to get JSON logs (for loading into centralized logging service). The FastAPI/Starlette?/uvicorn logging is using standard text-based logging. What is the best way to override that to use my structlog configuration?

Additional context

Here is my structlog setup if that is useful:

import sys
import logging

import structlog
import structlog._frames


def logging_setup(settings):
    log_level = settings.LOGGING

    logging.basicConfig(format="%(message)s", stream=sys.stdout, level=log_level)
    logger = logging.getLogger()
    logger.setLevel(log_level)

    def add_app_context(logger, method_name, event_dict):
        f, name = structlog._frames._find_first_app_frame_and_name(["logging", __name__])
        event_dict["file"] = f.f_code.co_filename
        event_dict["line"] = f.f_lineno
        event_dict["function"] = f.f_code.co_name
        return event_dict

    structlog.configure(
        logger_factory=structlog.stdlib.LoggerFactory(),
        processors=[
            structlog.stdlib.filter_by_level,
            structlog.stdlib.add_logger_name,
            structlog.stdlib.add_log_level,
            add_app_context,
            structlog.processors.TimeStamper(fmt="iso"),
            structlog.processors.JSONRenderer(indent=2, sort_keys=True),
        ],
    )

I'm getting a mix of logging results:

pynanopubstore    | Checking for script in /app/prestart.sh
pynanopubstore    | There is no script /app/prestart.sh
pynanopubstore    | INFO: Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit)
pynanopubstore    | INFO: Started reloader process [1]
pynanopubstore    | 2019-03-12 19:05.17 Using config file: None
pynanopubstore    | HEAD http://dev.biodati.test:9200/nanopubstore [status:200 request:0.006s]
pynanopubstore    | Started server process [9]
pynanopubstore    | Waiting for application startup.


pynanopubstore    | ('172.18.0.5', 32940) - "GET /docs HTTP/1.1" 200
pynanopubstore    | ('172.18.0.5', 32940) - "GET /openapi.json HTTP/1.1" 200
pynanopubstore    | {
pynanopubstore    |   "doc_type": "nanopub",
pynanopubstore    |   "event": "will do ES search",
pynanopubstore    |   "file": "./services/search.py",
pynanopubstore    |   "from_": 0,
pynanopubstore    |   "function": "es_search",
pynanopubstore    |   "index": "nanopubstore",
pynanopubstore    |   "level": "info",
pynanopubstore    |   "line": 247,
pynanopubstore    |   "logger": "services.search",
pynanopubstore    |   "query": {
pynanopubstore    |     "query_string": {
pynanopubstore    |       "allow_leading_wildcard": true,
pynanopubstore    |       "default_field": "*",
pynanopubstore    |       "query": "this OR that"
pynanopubstore    |     }
pynanopubstore    |   },
pynanopubstore    |   "size": 50,
pynanopubstore    |   "timestamp": "2019-03-12T19:05:26.385863Z"
pynanopubstore    | }
pynanopubstore    | POST http://dev.biodati.test:9200/nanopubstore/nanopub/_search?size=50&from=0 [status:200 request:0.393s]
pynanopubstore    | ('172.18.0.5', 32940) - "POST /search HTTP/1.1" 200

SQLAlchemy QueuePool limits

I'm trying to follow the SQLAlchemy part of the tutorial but I'm having problems because after several requests I'm running out of sessions. Clearly I should be closing them at some point, but I don't quite know how or when. This is also my first time using SQLAlchemy, so that doesn't help. :)

Should I be using the Starlette database middleware instead? If so, can I still integrate it with the nice SQLAlchemy models?

Peewee usage

Description

How can I use peewee ORM with fastapi?

Additional context

I'd love to use peewee rather than sqlalchemy for my API. However, all documentation seems to be oriented to SQLAlchemy.

It seems like I can use peewee directly up to a certain point, however some stuff (like ForeignKey relations) don't really work by default.

How to pass list in query parameters?

Description

I need to support several query parameters with same name in GET route. Typical request looks like this:
http://localhost/item?num=1&num=2

I configured a route

@app.get("/item", content_type=UJSONResponse)
async def get_part(num: list):
    found = False
    for i in num:
         if i in some_data:
             found = True
             break
    return {"found": found}

The idea is, that I can pass several numbers to check, if they exist in some_data. In my opinion, it is not a good idea to get numbers from body here, because it is a simple GET request, but in current version application expects argument "num" in body, so on my request (written above) I receive a response

{"detail":[{"loc":["body","num"],"msg":"field required","type":"value_error.missing"}]}

As I know, it is not restricted to pass several query parameters with the same name in an HTTP request by any specifications, so I would like to ask, is it possible to configure a route in FastAPI, which will be able to parse several query parameters with the same name?

Nesting FastAPI instances doesn't work very well

Do this:

main_app = FastAPI()
sub_api = FastAPI()

...
main_app.router.routes.append(Mount('/subapi', app=sub_api))

sub_api will correctly serve ever /subapi -- docs, methods, all that. However, the docs will still look for /openapi.json (absolute link) when trying to load the openapi spec. Additionally, the spec will not be adjusted to have the correct links, relative to where the module is mounted.

Perhaps this is a corner use case, but a lot of apps might have different collections of routes mounted in different subpaths.

How use by_alias argument in jsonable_encoder

API that I'm implementing requires from me input and output in camel case style. But I want to use snake case in my python code and follow pep8. So I tried allow_population_by_alias in my config class from the pydantic model and it works as expected without fastapi. But in fastapi, I can get the right response only when I set by_alias in jsonable_encoder to true. Is it possible to use this argument somehow from route decorators? I know that pydantic docs says that it's bad practice to use allow_population_by_alias, but I don't know another way to implement the specification as I want. The best way will be to have in fastapi or pydantic decorator for auto-renaming fields like this in serde for rust, but it's not important for me now. I just want my code to pass these tests now:

import json
from typing import List

from fastapi import FastAPI
from pydantic import BaseModel, Schema

import pytest
from starlette.testclient import TestClient

app = FastAPI()


class Model(BaseModel):
    model_field: str = Schema(..., alias="modelField")

    class Config:
        allow_population_by_alias = True


@app.get("/models", response_model=List[Model])
def get_models():
    m = Model(model_field="string")
    return [
        Model(model_field=m.model_field),
        Model(modelField=m.model_field),
        Model(**m.dict()),
        Model(**m.dict(by_alias=True)),
    ]


@app.post("/models", response_model=Model)
def create_model(m: Model):
    return Model(**m.dict())


@pytest.fixture
def client():
    yield TestClient(app)


def test_get_models(client):
    with client:
        response = client.get("/models")
    assert response.json() == [{"modelField": "string"} for _ in range(4)]


def test_create_model(client):
    with client:
        response = client.post("/models", json.dumps({"modelField": "string"}))
    assert response.json() == {"modelField": "string"}

Contextmanager as dependency

Depends() works great, but does not support post-response cleanup. How about injecting from context managers? Taking the SQL Db example, it would not need middleware anymore:

from contextlib import contextmanager

@contextmanager
def db() -> Session:
    db_session = Session()
    try:
        yield db_session
    finally:
        db_session.close()

@app.get("/users/{user_id}")
def read_user(user_id: int, db_session: Session = Depends(db)):
    user = get_user(db_session, user_id=user_id)
    return user

Middlewares do the job and they have been there forever, but from the FastAPI idiomatic approach request.scope looks a bit of a "dumpster" - it's not known in advance what to expect there without examining middlware docs/code. Middlewares also need to take care not to step on each other. And finally, not all paths may need that request.scope.db. By supporting contextmanager injection, I believe code can be more idiomatic, and more efficient (e.g. because we'll only initialize Session() for requests that need it).

What do you think?

Implementation wise, I guess dependencies.utils.solve_dependencies needs to return ctxmgrs in addition to values and errors. Then during execution we can use either contextlib.ExitStack or contextlib.AsyncExitStack (the latter is Python 3.7 only though) to wrap the execution with.
Also, since a "thing" passed to Depends() can be both callable and context manager, may be it worth having new DependsContextMgr() class. And finally, I think there should be some type checking to make sure that only async context managers are injected into async request handlers and vise-versa.

I can work on a PR for this if the initiative is going to be accepted.

FastAPI debugging

Hello
I've seen on FastAPI documentation site, that it integrates nice with a VSCode.
Any hint on debugging an FastAPI application in VSCode? There are some exaple configrations for python projects, but unfortunately I'm not able to utilize these for FastAPI

Thanks
Olegas

Response Model for Exceptions

This is a splinter conversation from #16

The docs for FastAPI say that you should raise an exception when an error occurs in your route logic, not return an object. Per #39, it seems like an elegant way to handle errors and I like it a lot.

In following with that pattern, I wonder if it is possible to extend the @app.exception_handler decorator to specify a Pydantic object that will be returned, the same way a standard @app.get decorator would? This might make the feature requested in #16 a little cleaner -- you could specify a list of codes that a route might throw, and the API documentation generator could figure it out from there.

Example code based on the suggestion:

from fastapi import FastAPI
from pydantic import BaseModel

class Item(BaseModel):
    name: str

class GenericHTTPError(BaseModel):
    status_code: int
    detail: str

items = [Item(name="foo"), Item(name='bar')]

app = FastAPI()

@app.get('/items/{item_id}', response_model=Item, errors=[404])
async def get_item(item_id: int):
    if item_id not in items:
        raise HTTPException(status_code=404, detail="Item not found")
    return {"item": items[item_id]}

@app.exception_handler(HTTPException, response_model=GenericHTTPError)
async def generic_error(request, ex):
    return GenericHTTPError(status_code=ex.status_code, detail=ex.detail)

I feel like I'm missing some details, but what do you think?

accessing the request object

In starlette you can access request object in function decorated with the route decorator.

it seems very handy to be able to access middlewares etc,
is there a way in fastapi to do that using the provided get/post/options.... decorators?
same question for the ApiRouter.

@app.route("/notes", methods=["GET"])
async def list_notes(request):
    query = notes.select()
    results = await request.database.fetchall(query)

tags at APIRouter level

Is your feature request related to a problem? Please describe.
I have a certain number of APIRouter, with for each of those a certain number of routes.
I want all routes that belong to a given APIRouter to have the same tag, and currently I'm achieving this by adding tags=['Tag1'] to each and every route.

Describe the solution you'd like
I got the feeling that I should be able to add the tags keyword directly to APIRouter and that all routes belonging to it inherit the tag.

Not sure it makes sense though but I feel it would avoid some useless repetition

file uploads > 65536 characters, binary blobs/mp4s etc.

Hello there,

First, I have to say that I am really impressed by what you guys are doing here. Also, your docs are outstanding and easy to follow. I spotted your project on my recommendations page and immediately fell in love, losing most of my Friday night tinkering around.

When I got to your file upload section in the docs, I hit a snag when trying to upload an mp4 file.

{ "detail": [ { "loc": [ "body", "file" ], "msg": "ensure this value has at most 65536 characters", "type": "value_error.any_str.max_length", "ctx": { "limit_value": 65536 } } ] }
image

It seems like the File type expects bytes as strings, which aren't allowed to go past 65536 characters in length. I expect others might run into this too, or be confused like I am when hitting this part.

-Russell

isort config inconsistent between local and travis

Describe the bug
if you run isort locally, it sorts the fastapi package as a 3rd party package, so once you push a branch on travis, isort there will fail
I think PyCQA/isort#541 (comment) describes it well

To Reproduce

Expected behavior
to run isort locally and have travis succeed without having to change fastapi import orders

Environment:

  • OS: debian buster
  • FastAPI Version [e.g. 0.3.0], get it with: 0.5.0
import fastapi
print(fastapi.__version__)
  • Python version, get it with:
python --version

Additional context
Add any other context about the problem here.

openapi_url incorrect from behind reverse proxy

The use case:

I'm running FastAPI behind a nginx reverse proxy, at the path http://localhost/api/. The documentation front ends (swagger and redoc) are expecting openapi.json at http://localhost/openapi.json but it's actually routed at http://localhost/api/openapi.json.

Setting openapi_url to /api/openapi.json also changes the openapi.json path, so now it's looking for (through the proxy) http://localhost/api/api/openapi.json.

Ideally, it'd be great if we could specify the "public path" for the api. That's how e.g. vue does it

Document test client

When switching from Flask / Flask_Restplus to FastAPI, tests fail
A basic test for a Flask RestPlus API will be like

class IntegrationTests(unittest.TestCase):
    def setUp(self):
        self.app = app.test_client()

    def test_base(self):
        response = self.app.get('/')
        self.assertEqual(response.status_code, 200)

but FastAPI don't have a test_client().

Solution
To have the tests pass, it would be nice, if FastAPI could provide a similar interface for Unittests (e.g. with Nose).

Docs for HTTP/2 support

Uvicorn is a great and fast HTTP/1 ASGI server that you recommend. I propose you recommend Hypercorn as a fast HTTP/2 ASGI server.

I'm the Hypercorn author, so naturally very biased. Also congratulations on the recognition of your great work.

Check json_encoders in pydantic model

Thanks for your awesome project. It's really great!
I have a model with a datetime field that needs to be in ISO 8601 format in the response. And when I call json method from the model instance I get the right string, but the response from fastapi returns standart output:

from datetime import datetime, timezone

from fastapi import FastAPI
from pydantic import BaseModel


class ModelWithDatetimeField(BaseModel):
    dt_field: datetime

    class Config:
        json_encoders = {
            datetime: lambda dt: dt.replace(
                microsecond=0, tzinfo=timezone.utc
            ).isoformat()
        }


app = FastAPI()
model = ModelWithDatetimeField(dt_field=datetime.utcnow())


@app.get("/model")
def get_model():
    print(model.json())
    return model

So, in console I'll get right string representation:
{"dt_field": "2019-01-31T22:00:12+00:00"}

But in the response from server I'll get this:
{"dt_field": "2019-01-31T22:00:12.810220"}

Security scopes

How can the scope parameter of the Security object be accessed?

Security accepts scopes as a parameter but the callable doesn't seem to be able to access it. The use case is essentially what is mentioned in the documentation - require certain scopes to be present to access an endpoint, or generate a 403 error.

What if you don't need the security parameter in the callback?

In the above use case, I'd like to require one of a set of scopes to be present but which one isn't really important. Using Security requires that a parameter be added like:

arg = Security(<callable>)

In callable(), test for the scopes and throw an HTTPException as needed. The problem is that arg isn't needed, so its ugly to pass it to the function. A much cleaner implementation would be to use a decorator - similar to the Starlette requires - like:

@requires(["user:read", "admin"])

Is this possible with the FastAPI design? If so, how does the decorate get passed a list of scopes (from the request)?

Clarify user_dict in Extra Models

Awesome project you have going on! Keep up the good work! 🎉

I was reading through the docs and in the Extra Models section, the About **user_dict sub-section doesn't explain why there is a difference between using **user_dict vs **user_in.dict().

Also I wasn't sure if you meant to specify user_dict as a generic dictionary for the section as that object doesn't exist in the above code example, or did you mean to also continue to use user_in?

WebSocket example is missing

Documentation states that FastAPI has support for WebSockets but there is no example on how to deal with it in code. Search on the internet is giving no results.

using external oAuth providers

Hi,

First of all, thank you for all the amazing work you've done with FastAPI. I'm just scratching the surface and I'm already amazed by what you're able to do right out of the box.

As for my question, At my institute we use a centralized authentication service for all webapps base on ORY Hydra. Is there an easy way built in to integrate an external server to authenticate again and use the token query the API?

Thanks
M

ending / for collections

Is your feature request related to a problem? Please describe.

If we use the following option with sub-routes

app.include_router(search_router, prefix="/items") 

We are forced to have an ending '/' on the collection endpoints, e.g. /items/ vs /items/{id} This is I think the more formal REST practice, but it results in a 404 when someone uses /items instead - I'd rather follow the mantra of 'be flexible with regards to user input and strict with what I return'. I'd like to have the option of allowing both /items and /items/ for the collection endpoints.

Describe the solution you'd like

I'd be happier to have a single route documented and the other route just work. I do like the setup of the prefixing of sub-routes - that's really convenient and powerful. But, I need to have the /items collection route option which prefix'ing doesn't allow for.

Describe alternatives you've considered

I can provide an /items endpoint without using the prefix option. I don't know if there is a better way to handle this though. I also managed to add two route decorators to get both /items and /items/ working as endpoints and documented in Swagger - unfortunately Swagger doesn't support synonymous routes yet. I'd be happier to have a single route documented and the other route just work.

Additional context

https://take.ms/fkG7N

Any plan to suppprt BackgroundTask from Starlette?

Starlette allows to attach a list of background tasks to a response, that will run only once the response has been sent.
If the task is a not a coroutine is it executed on a specific executor to not block the event loop.

    if task.is_coroutine():
        future = asyncio.ensure_future(task())
    else:
        loop = asyncio.get_event_loop()
        future = await loop.run_in_executor(None, task.func)

Is there any possibility to add this great feature to Fastai ?

Returning sqlalchemy Data Model

Hello,

First, thanks for all your docker project generators as well as the fantastic documentation. They have been super useful in getting my project off the ground.

I've been following the sqlalchemy example at: https://fastapi.tiangolo.com/tutorial/sql-databases/.

I'm trying to return a sqlalchemy data model, but I am getting the following error:

Fatal Python error: Cannot recover from stack overflow.

Thread 0x00007f115f510700 (most recent call first):
  File "/usr/local/lib/python3.7/concurrent/futures/thread.py", line 78 in _worker
  File "/usr/local/lib/python3.7/threading.py", line 865 in run
  File "/usr/local/lib/python3.7/threading.py", line 917 in _bootstrap_inner
  File "/usr/local/lib/python3.7/threading.py", line 885 in _bootstrap

Current thread 0x00007f116c905700 (most recent call first):
  File "/usr/local/lib/python3.7/site-packages/sqlalchemy/util/langhelpers.py", line 834 in __getattr__
  File "/usr/local/lib/python3.7/site-packages/sqlalchemy/util/langhelpers.py", line 836 in __getattr__
  File "/usr/local/lib/python3.7/site-packages/sqlalchemy/orm/attributes.py", line 190 in __getattr__
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 51 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 31 in <dictcomp>
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 30 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 31 in <dictcomp>
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 30 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 31 in <dictcomp>
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 30 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 31 in <dictcomp>
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 30 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 31 in <dictcomp>
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 30 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 31 in <dictcomp>
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 30 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 31 in <dictcomp>
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 30 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 31 in <dictcomp>
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 30 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 31 in <dictcomp>
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 30 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 31 in <dictcomp>
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 30 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 31 in <dictcomp>
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 30 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 31 in <dictcomp>
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 30 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 31 in <dictcomp>
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 30 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 31 in <dictcomp>
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 30 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 31 in <dictcomp>
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 30 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 31 in <dictcomp>
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 30 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 31 in <dictcomp>
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 30 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 31 in <dictcomp>
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 30 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 31 in <dictcomp>
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 30 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 31 in <dictcomp>
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 30 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 31 in <dictcomp>
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 30 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 31 in <dictcomp>
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 30 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 31 in <dictcomp>
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 30 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 31 in <dictcomp>
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 30 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 31 in <dictcomp>
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 30 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 31 in <dictcomp>
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 30 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 31 in <dictcomp>
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 30 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 59 in jsonable_encoder
  File "/usr/local/lib/python3.7/site-packages/fastapi/encoders.py", line 31 in <dictcomp>
  ...
INFO: Stopping reloader process [2468]

If I modify the fastapi specific code:
https://fastapi.tiangolo.com/tutorial/sql-databases/#create-your-fastapi-code

and parse the sqlalchemy response into a dictionary, it works fine:

@app.get("/users/{username}")
def read_user(username: str):
    user = get_user(username, db_session)
    user = {"name": user.id,  "email": user.email}
    return user

Thanks again. Been loving FastAPI so far.

graphql example

It would be useful to show how to accomplish integrating fastapi with graphene or another graphQL interface. Its touched on lightly in the docs, but I couldn't find a complete example that reuses the good parts of fastapi and pydantic.

Is this too far out of scope for a suggestion?

Use third party class as property in pydantic schema

Describe the bug
I have a pydantic schema that needs a third party class (bson.objectid.ObjectID) as a property. For this reason I created a custom validator and encoder as per pydantic documentation.

Code

from bson.objectid import ObjectId
from pydantic import BaseModel
from pydantic import validators
from pydantic.errors import PydanticTypeError
from pydantic.utils import change_exception

class ObjectIdError(PydanticTypeError):
    msg_template = 'value is not a valid bson.objectid.ObjectId'

def object_id_validator(v) -> ObjectId:
    with change_exception(ObjectIdError, ValueError):
        v = ObjectId(v)
    return v

def get_validators() -> None:
    yield validators.not_none_validator
    yield object_id_validator

ObjectId.__get_validators__ = get_validators

def encode_object_id(object_id: ObjectId):
    return str(object_id)

class UserId(BaseModel):
    object_id: ObjectId = None

    class Config:
        json_encoders = {
            ObjectId: encode_object_id
        }

class User(UserId):
    email: str
    salt: str
    hashed_password: str

# Just for testing
user = User(object_id = ObjectId(), email="[email protected]", salt="12345678", hashed_password="letmein")
print(user.json())
# Outputs:
# {"object_id": "5c7e424225e2971c8c548a86", "email": "[email protected]", "salt": "12345678", "hashed_password": "letmein"}


As you can see at the bottom of the code, the serialization seems to work just fine. But when I use this schema as an argument (and/or response type) in API operations and then open the automatic documentation, I get presented with an error.

Code

from bson import ObjectId
from fastapi import FastAPI
from user import User, UserId

app = FastAPI()


@app.post("/user", tags=["user"], response_model=UserId)
def create_user(user: User):
    # Create user and return id
    print(user)
    return UserId(objectId=ObjectId())

Log

INFO: ('127.0.0.1', 2706) - "GET /openapi.json HTTP/1.1" 500
ERROR: Exception in ASGI application
Traceback (most recent call last):
  File "<project-path>\venv\lib\site-packages\uvicorn\protocols\http\h11_impl.py", line 373, in run_asgi
    result = await asgi(self.receive, self.send)
  File "<project-path>\venv\lib\site-packages\uvicorn\middleware\debug.py", line 83, in __call__
    raise exc from None
  File "<project-path>\venv\lib\site-packages\uvicorn\middleware\debug.py", line 80, in __call__
    await asgi(receive, self.send)
  File "<project-path>\venv\lib\site-packages\starlette\middleware\errors.py", line 125, in asgi
    raise exc from None
  File "<project-path>\venv\lib\site-packages\starlette\middleware\errors.py", line 103, in asgi
    await asgi(receive, _send)
  File "<project-path>\venv\lib\site-packages\starlette\exceptions.py", line 74, in app
    raise exc from None
  File "<project-path>\venv\lib\site-packages\starlette\exceptions.py", line 63, in app
    await instance(receive, sender)
  File "<project-path>\venv\lib\site-packages\starlette\routing.py", line 43, in awaitable
    response = await run_in_threadpool(func, request)
  File "<project-path>\venv\lib\site-packages\starlette\concurrency.py", line 24, in run_in_threadpool
    return await loop.run_in_executor(None, func, *args)
  File "C:\Program Files (x86)\Python37-32\lib\concurrent\futures\thread.py", line 57, in run
    result = self.fn(*self.args, **self.kwargs)
  File "<project-path>\venv\lib\site-packages\fastapi\applications.py", line 83, in <lambda>
    lambda req: JSONResponse(self.openapi()),
  File "<project-path>\venv\lib\site-packages\fastapi\applications.py", line 75, in openapi
    openapi_prefix=self.openapi_prefix,
  File "<project-path>\venv\lib\site-packages\fastapi\openapi\utils.py", line 230, in get_openapi
    flat_models=flat_models, model_name_map=model_name_map
  File "<project-path>\venv\lib\site-packages\fastapi\utils.py", line 45, in get_model_definitions
    model, model_name_map=model_name_map, ref_prefix=REF_PREFIX
  File "<project-path>\venv\lib\site-packages\pydantic\schema.py", line 461, in model_process_schema
    model, by_alias=by_alias, model_name_map=model_name_map, ref_prefix=ref_prefix
  File "<project-path>\venv\lib\site-packages\pydantic\schema.py", line 482, in model_type_schema
    f, by_alias=by_alias, model_name_map=model_name_map, ref_prefix=ref_prefix
  File "<project-path>\venv\lib\site-packages\pydantic\schema.py", line 238, in field_schema
    ref_prefix=ref_prefix,
  File "<project-path>\venv\lib\site-packages\pydantic\schema.py", line 440, in field_type_schema
    ref_prefix=ref_prefix,
  File "<project-path>\venv\lib\site-packages\pydantic\schema.py", line 643, in field_singleton_schema
    raise ValueError(f'Value not declarable with JSON Schema, field: {field}')
ValueError: Value not declarable with JSON Schema, field: object_id type=ObjectId default=None


To Reproduce
Copy my code and follow the instrcutions given in the "Describe the bug" section.

Expected behavior
No error should occur and the documentation should be able to show the schema correctly.

Environment:

  • OS: Windows 10
  • FastAPI: 0.6.3
  • Python: 3.7.2

Creating Enum class from dictionary

I want the user to be able to select parameter values from an enum list of values as a dropdown in the swagger documentation.

The only way, to my knowledge, to do that is to create a Enum pydantic class, and designate a parameter as a part of that class. Which is fine. But in my situation, I want the enum to be dynamically generated based on all unique document values of a property stored in a MongoDB collection. I think you can create a BaseModel class dynamically using the create_model() function, but as far as I know that's not possible with a Enum class.

So my question is: How can I create a parameter enumeration in Swagger from all values in a mongDb collection?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.