diff --git a/docs/img/tutorial/response-model/image01.png b/docs/img/tutorial/response-model/image01.png new file mode 100644 index 000000000..d418e8cae Binary files /dev/null and b/docs/img/tutorial/response-model/image01.png differ diff --git a/docs/img/tutorial/response-model/image02.png b/docs/img/tutorial/response-model/image02.png new file mode 100644 index 000000000..dbb4f79c3 Binary files /dev/null and b/docs/img/tutorial/response-model/image02.png differ diff --git a/docs/tutorial/first-steps.md b/docs/tutorial/first-steps.md index 5572fc2e9..e45cab768 100644 --- a/docs/tutorial/first-steps.md +++ b/docs/tutorial/first-steps.md @@ -130,6 +130,19 @@ uvicorn main:my_awesome_api --debug The `@app.get("/")` tells **FastAPI** that the function right below is an endpoint and that it should go to the path route `/`. +You can also use other HTTP methods: + +* `@app.post()` +* `@app.put()` +* `@app.delete()` + +And more exotic ones: + +* `@app.options()` +* `@app.head()` +* `@app.patch()` +* `@app.trace()` + ### Step 4: define the endpoint function ```Python hl_lines="7" diff --git a/docs/tutorial/response-model.md b/docs/tutorial/response-model.md new file mode 100644 index 000000000..d339d9e0c --- /dev/null +++ b/docs/tutorial/response-model.md @@ -0,0 +1,84 @@ +You can declare the model used for the response with the parameter `response_model` in any of the endpoint creation methods: + +* `@app.get()` +* `@app.post()` +* `@app.put()` +* `@app.delete()` +* etc. + +```Python hl_lines="17" +{!./tutorial/src/response-model/tutorial001.py!} +``` + +!!! note + Notice that `response_model` is a parameter of the "decorator" method (`get`, `post`, etc), not of your endpoint function like all the parameters and body. + +It receives a standard Pydantic model and will: + +* Convert the output data to the type declarations of the model +* Validate the data +* Add a JSON Schema for the response, in the OpenAPI endpoint +* Will be used by the automatic documentation systems + +But most importantly: + +* Will limit the output data to that of the model. We'll see how that's important below. + +## Return the same input data + +Here we are declaring a `UserIn` model, it will contain a plaintext password: + +```Python hl_lines="8 10" +{!./tutorial/src/response-model/tutorial002.py!} +``` + +And we are using this model to declare our input and the same model to declare our output: + +```Python hl_lines="16 17" +{!./tutorial/src/response-model/tutorial002.py!} +``` + +Now, whenever a browser is creating a user with a password, the API will return the same password in the response. + +In this case, it might not be a problem, becase the user himself is sending the password. + +But if we use sthe same model for another endpoint, we could be sending the passwords of our users to every client. + +!!! danger + Never send the plain password of a user in a response. + +## Add an output model + +We can instead create an input model with the plaintext password and an output model without it: + +```Python hl_lines="8 10 15" +{!./tutorial/src/response-model/tutorial003.py!} +``` + +Here, even though our endpoint function is returning the same input user that contains the password: + +```Python hl_lines="23" +{!./tutorial/src/response-model/tutorial003.py!} +``` + +...we declared the `response_model` to be our model `UserOut`, that doesn't include the password: + +```Python hl_lines="21" +{!./tutorial/src/response-model/tutorial003.py!} +``` + +So, **FastAPI** will take care of filtering out all the data that is not declared in the output model (using Pydantic). + +## See it in the docs + +When you see the automatic docs, you can check that the input model and output model will both have their own JSON Schema: + + + +And both models will be used for the interactive API documentation: + + + +## Recap + +Use the endpoint decorator's parameter `response_model` to define response models and especially to ensure private data is filtered out. diff --git a/docs/tutorial/src/response-model/tutorial001.py b/docs/tutorial/src/response-model/tutorial001.py new file mode 100644 index 000000000..86dadcbda --- /dev/null +++ b/docs/tutorial/src/response-model/tutorial001.py @@ -0,0 +1,19 @@ +from typing import Set + +from fastapi import FastAPI +from pydantic import BaseModel + +app = FastAPI() + + +class Item(BaseModel): + name: str + description: str = None + price: float + tax: float = None + tags: Set[str] = [] + + +@app.post("/items/", response_model=Item) +async def create_item(*, item: Item): + return item diff --git a/docs/tutorial/src/response-model/tutorial002.py b/docs/tutorial/src/response-model/tutorial002.py new file mode 100644 index 000000000..3fb475b9d --- /dev/null +++ b/docs/tutorial/src/response-model/tutorial002.py @@ -0,0 +1,18 @@ +from fastapi import FastAPI +from pydantic import BaseModel +from pydantic.types import EmailStr + +app = FastAPI() + + +class UserIn(BaseModel): + username: str + password: str + email: EmailStr + full_name: str = None + + +# Don't do this in production! +@app.post("/user/", response_model=UserIn) +async def create_user(*, user: UserIn): + return user diff --git a/docs/tutorial/src/response-model/tutorial003.py b/docs/tutorial/src/response-model/tutorial003.py new file mode 100644 index 000000000..c8ea361d8 --- /dev/null +++ b/docs/tutorial/src/response-model/tutorial003.py @@ -0,0 +1,23 @@ +from fastapi import FastAPI +from pydantic import BaseModel +from pydantic.types import EmailStr + +app = FastAPI() + + +class UserIn(BaseModel): + username: str + password: str + email: EmailStr + full_name: str = None + + +class UserOut(BaseModel): + username: str + email: EmailStr + full_name: str = None + + +@app.post("/user/", response_model=UserOut) +async def create_user(*, user: UserIn): + return user diff --git a/mkdocs.yml b/mkdocs.yml index e031b15a3..7770d5fe5 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -29,6 +29,7 @@ nav: - Body - Nested Models: 'tutorial/body-nested-models.md' - Cookie Parameters: 'tutorial/cookie-params.md' - Header Parameters: 'tutorial/header-params.md' + - Response Model: 'tutorial/response-model.md' - Concurrency and async / await: 'async.md' - Deployment: 'deployment.md'