committed by
GitHub
246 changed files with 7270 additions and 2218 deletions
@ -0,0 +1,63 @@ |
|||
# JSON with Bytes as Base64 { #json-with-bytes-as-base64 } |
|||
|
|||
If your app needs to receive and send JSON data, but you need to include binary data in it, you can encode it as base64. |
|||
|
|||
## Base64 vs Files { #base64-vs-files } |
|||
|
|||
Consider first if you can use [Request Files](../tutorial/request-files.md) for uploading binary data and [Custom Response - FileResponse](./custom-response.md#fileresponse--fileresponse-) for sending binary data, instead of encoding it in JSON. |
|||
|
|||
JSON can only contain UTF-8 encoded strings, so it can't contain raw bytes. |
|||
|
|||
Base64 can encode binary data in strings, but to do it, it needs to use more characters than the original binary data, so it would normally be less efficient than regular files. |
|||
|
|||
Use base64 only if you definitely need to include binary data in JSON, and you can't use files for that. |
|||
|
|||
## Pydantic `bytes` { #pydantic-bytes } |
|||
|
|||
You can declare a Pydantic model with `bytes` fields, and then use `val_json_bytes` in the model config to tell it to use base64 to *validate* input JSON data, as part of that validation it will decode the base64 string into bytes. |
|||
|
|||
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:9,29:35] hl[9] *} |
|||
|
|||
If you check the `/docs`, they will show that the field `data` expects base64 encoded bytes: |
|||
|
|||
<div class="screenshot"> |
|||
<img src="/img/tutorial/json-base64-bytes/image01.png"> |
|||
</div> |
|||
|
|||
You could send a request like: |
|||
|
|||
```json |
|||
{ |
|||
"description": "Some data", |
|||
"data": "aGVsbG8=" |
|||
} |
|||
``` |
|||
|
|||
/// tip |
|||
|
|||
`aGVsbG8=` is the base64 encoding of `hello`. |
|||
|
|||
/// |
|||
|
|||
And then Pydantic will decode the base64 string and give you the original bytes in the `data` field of the model. |
|||
|
|||
You will receive a response like: |
|||
|
|||
```json |
|||
{ |
|||
"description": "Some data", |
|||
"content": "hello" |
|||
} |
|||
``` |
|||
|
|||
## Pydantic `bytes` for Output Data { #pydantic-bytes-for-output-data } |
|||
|
|||
You can also use `bytes` fields with `ser_json_bytes` in the model config for output data, and Pydantic will *serialize* the bytes as base64 when generating the JSON response. |
|||
|
|||
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:2,12:16,29,38:41] hl[16] *} |
|||
|
|||
## Pydantic `bytes` for Input and Output Data { #pydantic-bytes-for-input-and-output-data } |
|||
|
|||
And of course, you can use the same model configured to use base64 to handle both input (*validate*) with `val_json_bytes` and output (*serialize*) with `ser_json_bytes` when receiving and sending JSON data. |
|||
|
|||
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:2,19:26,29,44:46] hl[23:26] *} |
|||
@ -0,0 +1,117 @@ |
|||
# Stream Data { #stream-data } |
|||
|
|||
If you want to stream data that can be structured as JSON, you should [Stream JSON Lines](../tutorial/stream-json-lines.md). |
|||
|
|||
But if you want to **stream pure binary data** or strings, here's how you can do it. |
|||
|
|||
/// info |
|||
|
|||
Added in FastAPI 0.134.0. |
|||
|
|||
/// |
|||
|
|||
## Use Cases { #use-cases } |
|||
|
|||
You could use this if you want to stream pure strings, for example directly from the output of an **AI LLM** service. |
|||
|
|||
You could also use it to stream **large binary files**, where you stream each chunk of data as you read it, without having to read it all in memory at once. |
|||
|
|||
You could also stream **video** or **audio** this way, it could even be generated as you process and send it. |
|||
|
|||
## A `StreamingResponse` with `yield` { #a-streamingresponse-with-yield } |
|||
|
|||
If you declare a `response_class=StreamingResponse` in your *path operation function*, you can use `yield` to send each chunk of data in turn. |
|||
|
|||
{* ../../docs_src/stream_data/tutorial001_py310.py ln[1:23] hl[20,23] *} |
|||
|
|||
FastAPI will give each chunk of data to the `StreamingResponse` as is, it won't try to convert it to JSON or anything similar. |
|||
|
|||
### Non-async *path operation functions* { #non-async-path-operation-functions } |
|||
|
|||
You can also use regular `def` functions (without `async`), and use `yield` the same way. |
|||
|
|||
{* ../../docs_src/stream_data/tutorial001_py310.py ln[26:29] hl[27] *} |
|||
|
|||
### No Annotation { #no-annotation } |
|||
|
|||
You don't really need to declare the return type annotation for streaming binary data. |
|||
|
|||
As FastAPI will not try to convert the data to JSON with Pydantic or serialize it in any way, in this case, the type annotation is only for your editor and tools to use, it won't be used by FastAPI. |
|||
|
|||
{* ../../docs_src/stream_data/tutorial001_py310.py ln[32:35] hl[33] *} |
|||
|
|||
This also means that with `StreamingResponse` you have the **freedom** and **responsibility** to produce and encode the data bytes exactly as you need them to be sent, independent of the type annotations. 🤓 |
|||
|
|||
### Stream Bytes { #stream-bytes } |
|||
|
|||
One of the main use cases would be to stream `bytes` instead of strings, you can of course do it. |
|||
|
|||
{* ../../docs_src/stream_data/tutorial001_py310.py ln[44:47] hl[47] *} |
|||
|
|||
## A Custom `PNGStreamingResponse` { #a-custom-pngstreamingresponse } |
|||
|
|||
In the examples above, the data bytes were streamed, but the response didn't have a `Content-Type` header, so the client didn't know what type of data it was receiving. |
|||
|
|||
You can create a custom sub-class of `StreamingResponse` that sets the `Content-Type` header to the type of data you're streaming. |
|||
|
|||
For example, you can create a `PNGStreamingResponse` that sets the `Content-Type` header to `image/png` using the `media_type` attribute: |
|||
|
|||
{* ../../docs_src/stream_data/tutorial002_py310.py ln[6,19:20] hl[20] *} |
|||
|
|||
Then you can use this new class in `response_class=PNGStreamingResponse` in your *path operation function*: |
|||
|
|||
{* ../../docs_src/stream_data/tutorial002_py310.py ln[23:27] hl[23] *} |
|||
|
|||
### Simulate a File { #simulate-a-file } |
|||
|
|||
In this example, we are simulating a file with `io.BytesIO`, which is a file-like object that lives only in memory, but lets us use the same interface. |
|||
|
|||
For example, we can iterate over it to consume its contents, as we could with a file. |
|||
|
|||
{* ../../docs_src/stream_data/tutorial002_py310.py ln[1:27] hl[3,12:13,25] *} |
|||
|
|||
/// note | Technical Details |
|||
|
|||
The other two variables, `image_base64` and `binary_image`, are an image encoded in Base64, and then converted to bytes, to then pass it to `io.BytesIO`. |
|||
|
|||
Only so that it can live in the same file for this example and you can copy it and run it as is. 🥚 |
|||
|
|||
/// |
|||
|
|||
By using a `with` block, we make sure that the file-like object is closed after the generator function (the function with `yield`) is done. So, after it finishes sending the response. |
|||
|
|||
It wouldn't be that important in this specific example because it's a fake in-memory file (with `io.BytesIO`), but with a real file, it would be important to make sure the file is closed after the work with it is done. |
|||
|
|||
### Files and Async { #files-and-async } |
|||
|
|||
In most cases, file-like objects are not compatible with async and await by default. |
|||
|
|||
For example, they don't have an `await file.read()`, or `async for chunk in file`. |
|||
|
|||
And in many cases, reading them would be a blocking operation (that could block the event loop), because they are read from disk or from the network. |
|||
|
|||
/// info |
|||
|
|||
The example above is actually an exception, because the `io.BytesIO` object is already in memory, so reading it won't block anything. |
|||
|
|||
But in many cases reading a file or a file-like object would block. |
|||
|
|||
/// |
|||
|
|||
To avoid blocking the event loop, you can simply declare the *path operation function* with regular `def` instead of `async def`, that way FastAPI will run it on a threadpool worker, to avoid blocking the main loop. |
|||
|
|||
{* ../../docs_src/stream_data/tutorial002_py310.py ln[30:34] hl[31] *} |
|||
|
|||
/// tip |
|||
|
|||
If you need to call blocking code from inside of an async function, or an async function from inside of a blocking function, you could use [Asyncer](https://asyncer.tiangolo.com), a sibling library to FastAPI. |
|||
|
|||
/// |
|||
|
|||
### `yield from` { #yield-from } |
|||
|
|||
When you are iterating over something, like a file-like object, and then you are doing `yield` for each item, you could also use `yield from` to yield each item directly and skip the `for` loop. |
|||
|
|||
This is not particular to FastAPI, it's just Python, but it's a nice trick to know. 😎 |
|||
|
|||
{* ../../docs_src/stream_data/tutorial002_py310.py ln[37:40] hl[40] *} |
|||
@ -0,0 +1,88 @@ |
|||
# Strict Content-Type Checking { #strict-content-type-checking } |
|||
|
|||
By default, **FastAPI** uses strict `Content-Type` header checking for JSON request bodies, this means that JSON requests **must** include a valid `Content-Type` header (e.g. `application/json`) in order for the body to be parsed as JSON. |
|||
|
|||
## CSRF Risk { #csrf-risk } |
|||
|
|||
This default behavior provides protection against a class of **Cross-Site Request Forgery (CSRF)** attacks in a very specific scenario. |
|||
|
|||
These attacks exploit the fact that browsers allow scripts to send requests without doing any CORS preflight check when they: |
|||
|
|||
* don't have a `Content-Type` header (e.g. using `fetch()` with a `Blob` body) |
|||
* and don't send any authentication credentials. |
|||
|
|||
This type of attack is mainly relevant when: |
|||
|
|||
* the application is running locally (e.g. on `localhost`) or in an internal network |
|||
* and the application doesn't have any authentication, it expects that any request from the same network can be trusted. |
|||
|
|||
## Example Attack { #example-attack } |
|||
|
|||
Imagine you build a way to run a local AI agent. |
|||
|
|||
It provides an API at |
|||
|
|||
``` |
|||
http://localhost:8000/v1/agents/multivac |
|||
``` |
|||
|
|||
There's also a frontend at |
|||
|
|||
``` |
|||
http://localhost:8000 |
|||
``` |
|||
|
|||
/// tip |
|||
|
|||
Note that both have the same host. |
|||
|
|||
/// |
|||
|
|||
Then using the frontend you can make the AI agent do things on your behalf. |
|||
|
|||
As it's running **locally**, and not in the open internet, you decide to **not have any authentication** set up, just trusting the access to the local network. |
|||
|
|||
Then one of your users could install it and run it locally. |
|||
|
|||
Then they could open a malicious website, e.g. something like |
|||
|
|||
``` |
|||
https://evilhackers.example.com |
|||
``` |
|||
|
|||
And that malicious website sends requests using `fetch()` with a `Blob` body to the local API at |
|||
|
|||
``` |
|||
http://localhost:8000/v1/agents/multivac |
|||
``` |
|||
|
|||
Even though the host of the malicious website and the local app is different, the browser won't trigger a CORS preflight request because: |
|||
|
|||
* It's running without any authentication, it doesn't have to send any credentials. |
|||
* The browser thinks it's not sending JSON (because of the missing `Content-Type` header). |
|||
|
|||
Then the malicious website could make the local AI agent send angry messages to the user's ex-boss... or worse. 😅 |
|||
|
|||
## Open Internet { #open-internet } |
|||
|
|||
If your app is in the open internet, you wouldn't "trust the network" and let anyone send privileged requests without authentication. |
|||
|
|||
Attackers could simply run a script to send requests to your API, no need for browser interaction, so you are probably already securing any privileged endpoints. |
|||
|
|||
In that case **this attack / risk doesn't apply to you**. |
|||
|
|||
This risk and attack is mainly relevant when the app runs on the **local network** and that is the **only assumed protection**. |
|||
|
|||
## Allowing Requests Without Content-Type { #allowing-requests-without-content-type } |
|||
|
|||
If you need to support clients that don't send a `Content-Type` header, you can disable strict checking by setting `strict_content_type=False`: |
|||
|
|||
{* ../../docs_src/strict_content_type/tutorial001_py310.py hl[4] *} |
|||
|
|||
With this setting, requests without a `Content-Type` header will have their body parsed as JSON, which is the same behavior as older versions of FastAPI. |
|||
|
|||
/// info |
|||
|
|||
This behavior and configuration was added in FastAPI 0.132.0. |
|||
|
|||
/// |
|||
@ -0,0 +1,23 @@ |
|||
# Editor Support { #editor-support } |
|||
|
|||
The official [FastAPI Extension](https://marketplace.visualstudio.com/items?itemName=FastAPILabs.fastapi-vscode) enhances your FastAPI development workflow with *path operation* discovery, navigation, as well as FastAPI Cloud deployment, and live log streaming. |
|||
|
|||
For more details about the extension, refer to the README on the [GitHub repository](https://github.com/fastapi/fastapi-vscode). |
|||
|
|||
## Setup and Installation { #setup-and-installation } |
|||
|
|||
The **FastAPI Extension** is available for both [VS Code](https://code.visualstudio.com/) and [Cursor](https://www.cursor.com/). It can be installed directly from the Extensions panel in each editor by searching for "FastAPI" and selecting the extension published by **FastAPI Labs**. The extension also works in browser-based editors such as [vscode.dev](https://vscode.dev) and [github.dev](https://github.dev). |
|||
|
|||
### Application Discovery { #application-discovery } |
|||
|
|||
By default, the extension will automatically discover FastAPI applications in your workspace by scanning for files that instantiate `FastAPI()`. If auto-detection doesn't work for your project structure, you can specify an entrypoint via `[tool.fastapi]` in `pyproject.toml` or the `fastapi.entryPoint` VS Code setting using module notation (e.g. `myapp.main:app`). |
|||
|
|||
## Features { #features } |
|||
|
|||
- **Path Operation Explorer** - A sidebar tree view of all <dfn title="routes, endpoints">*path operations*</dfn> in your application. Click to jump to any route or router definition. |
|||
- **Route Search** - Search by path, method, or name with <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>E</kbd> (on macOS: <kbd>Cmd</kbd> + <kbd>Shift</kbd> + <kbd>E</kbd>). |
|||
- **CodeLens Navigation** - Clickable links above test client calls (e.g. `client.get('/items')`) that jump to the matching *path operation* for quick navigation between tests and implementation. |
|||
- **Deploy to FastAPI Cloud** - One-click deployment of your app to [FastAPI Cloud](https://fastapicloud.com/). |
|||
- **Stream Application Logs** - Real-time log streaming from your FastAPI Cloud-deployed application with level filtering and text search. |
|||
|
|||
If you'd like to familiarize yourself with the extension's features, you can checkout the extension walkthrough by opening the Command Palette (<kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>P</kbd> or on macOS: <kbd>Cmd</kbd> + <kbd>Shift</kbd> + <kbd>P</kbd>) and selecting "Welcome: Open walkthrough..." and then choosing the "Get started with FastAPI" walkthrough. |
|||
@ -1,7 +1,7 @@ |
|||
# Testing a Database { #testing-a-database } |
|||
|
|||
You can study about databases, SQL, and SQLModel in the <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">SQLModel docs</a>. 🤓 |
|||
You can study about databases, SQL, and SQLModel in the [SQLModel docs](https://sqlmodel.tiangolo.com/). 🤓 |
|||
|
|||
There's a mini <a href="https://sqlmodel.tiangolo.com/tutorial/fastapi/" class="external-link" target="_blank">tutorial on using SQLModel with FastAPI</a>. ✨ |
|||
There's a mini [tutorial on using SQLModel with FastAPI](https://sqlmodel.tiangolo.com/tutorial/fastapi/). ✨ |
|||
|
|||
That tutorial includes a section about <a href="https://sqlmodel.tiangolo.com/tutorial/fastapi/tests/" class="external-link" target="_blank">testing SQL databases</a>. 😎 |
|||
That tutorial includes a section about [testing SQL databases](https://sqlmodel.tiangolo.com/tutorial/fastapi/tests/). 😎 |
|||
|
|||
|
After Width: | Height: | Size: 71 KiB |
@ -0,0 +1,29 @@ |
|||
document.addEventListener("DOMContentLoaded", function () { |
|||
var script = document.createElement("script"); |
|||
script.src = "https://widget.kapa.ai/kapa-widget.bundle.js"; |
|||
script.setAttribute("data-website-id", "91f47f27-b405-4299-bf5f-a1c0ec07b3cc"); |
|||
script.setAttribute("data-project-name", "FastAPI"); |
|||
script.setAttribute("data-project-color", "#009485"); |
|||
script.setAttribute("data-project-logo", "https://fastapi.tiangolo.com/img/favicon.png"); |
|||
script.setAttribute("data-bot-protection-mechanism", "hcaptcha"); |
|||
script.setAttribute("data-button-height", "3rem"); |
|||
script.setAttribute("data-button-width", "3rem"); |
|||
script.setAttribute("data-button-border-radius", "50%"); |
|||
script.setAttribute("data-button-padding", "0"); |
|||
script.setAttribute("data-button-image", "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 8V4H8'/%3E%3Crect width='16' height='12' x='4' y='8' rx='2'/%3E%3Cpath d='M2 14h2'/%3E%3Cpath d='M20 14h2'/%3E%3Cpath d='M15 13v2'/%3E%3Cpath d='M9 13v2'/%3E%3C/svg%3E"); |
|||
script.setAttribute("data-button-image-height", "20px"); |
|||
script.setAttribute("data-button-image-width", "20px"); |
|||
script.setAttribute("data-button-text", "Ask AI"); |
|||
script.setAttribute("data-button-text-font-size", "0.5rem"); |
|||
script.setAttribute("data-button-text-font-family", "Roboto, sans-serif"); |
|||
script.setAttribute("data-button-text-color", "#FFFFFF"); |
|||
script.setAttribute("data-modal-border-radius", "0.5rem"); |
|||
script.setAttribute("data-modal-header-bg-color", "#009485"); |
|||
script.setAttribute("data-modal-title", "FastAPI AI Assistant"); |
|||
script.setAttribute("data-modal-title-color", "#FFFFFF"); |
|||
script.setAttribute("data-modal-title-font-family", "Roboto, sans-serif"); |
|||
script.setAttribute("data-modal-example-questions", "How to define a route?,How to validate models?,How to handle responses?,How to deploy FastAPI?"); |
|||
script.setAttribute("data-modal-disclaimer", "AI-generated answers based on FastAPI [documentation](https://fastapi.tiangolo.com/) and [community discussions](https://github.com/fastapi/fastapi/discussions). Always verify important information."); |
|||
script.async = true; |
|||
document.head.appendChild(script); |
|||
}); |
|||
File diff suppressed because it is too large
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue