Browse Source
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>pull/15157/head
committed by
GitHub
6 changed files with 522 additions and 0 deletions
@ -0,0 +1,63 @@ |
|||
# JSON con Bytes como Base64 { #json-with-bytes-as-base64 } |
|||
|
|||
Si tu app necesita recibir y enviar datos JSON, pero necesitas incluir datos binarios en él, puedes codificarlos como base64. |
|||
|
|||
## Base64 vs Archivos { #base64-vs-files } |
|||
|
|||
Considera primero si puedes usar [Archivos en request](../tutorial/request-files.md) para subir datos binarios y [Response personalizada - FileResponse](./custom-response.md#fileresponse--fileresponse-) para enviar datos binarios, en lugar de codificarlos en JSON. |
|||
|
|||
JSON solo puede contener strings codificados en UTF-8, así que no puede contener bytes crudos. |
|||
|
|||
Base64 puede codificar datos binarios en strings, pero para hacerlo necesita usar más caracteres que los datos binarios originales, así que normalmente sería menos eficiente que los archivos normales. |
|||
|
|||
Usa base64 solo si definitivamente necesitas incluir datos binarios en JSON y no puedes usar archivos para eso. |
|||
|
|||
## Pydantic `bytes` { #pydantic-bytes } |
|||
|
|||
Puedes declarar un modelo de Pydantic con campos `bytes`, y luego usar `val_json_bytes` en la configuración del modelo para indicarle que use base64 para validar datos JSON de entrada; como parte de esa validación decodificará el string base64 en bytes. |
|||
|
|||
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:9,29:35] hl[9] *} |
|||
|
|||
Si revisas `/docs`, verás que el campo `data` espera bytes codificados en base64: |
|||
|
|||
<div class="screenshot"> |
|||
<img src="/img/tutorial/json-base64-bytes/image01.png"> |
|||
</div> |
|||
|
|||
Podrías enviar un request como: |
|||
|
|||
```json |
|||
{ |
|||
"description": "Some data", |
|||
"data": "aGVsbG8=" |
|||
} |
|||
``` |
|||
|
|||
/// tip | Consejo |
|||
|
|||
`aGVsbG8=` es la codificación base64 de `hello`. |
|||
|
|||
/// |
|||
|
|||
Y luego Pydantic decodificará el string base64 y te dará los bytes originales en el campo `data` del modelo. |
|||
|
|||
Recibirás una response como: |
|||
|
|||
```json |
|||
{ |
|||
"description": "Some data", |
|||
"content": "hello" |
|||
} |
|||
``` |
|||
|
|||
## Pydantic `bytes` para datos de salida { #pydantic-bytes-for-output-data } |
|||
|
|||
También puedes usar campos `bytes` con `ser_json_bytes` en la configuración del modelo para datos de salida, y Pydantic serializará los bytes como base64 al generar la response JSON. |
|||
|
|||
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:2,12:16,29,38:41] hl[16] *} |
|||
|
|||
## Pydantic `bytes` para datos de entrada y salida { #pydantic-bytes-for-input-and-output-data } |
|||
|
|||
Y por supuesto, puedes usar el mismo modelo configurado para usar base64 para manejar tanto la entrada (*validate*) con `val_json_bytes` como la salida (*serialize*) con `ser_json_bytes` al recibir y enviar datos JSON. |
|||
|
|||
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:2,19:26,29,44:46] hl[23:26] *} |
|||
@ -0,0 +1,117 @@ |
|||
# Transmitir datos { #stream-data } |
|||
|
|||
Si quieres transmitir datos que se puedan estructurar como JSON, deberías [Transmitir JSON Lines](../tutorial/stream-json-lines.md). |
|||
|
|||
Pero si quieres transmitir datos binarios puros o strings, aquí tienes cómo hacerlo. |
|||
|
|||
/// info | Información |
|||
|
|||
Añadido en FastAPI 0.134.0. |
|||
|
|||
/// |
|||
|
|||
## Casos de uso { #use-cases } |
|||
|
|||
Podrías usar esto si quieres transmitir strings puros, por ejemplo directamente de la salida de un servicio de AI LLM. |
|||
|
|||
También podrías usarlo para transmitir archivos binarios grandes, donde transmites cada bloque de datos a medida que lo lees, sin tener que leerlo todo en memoria de una sola vez. |
|||
|
|||
También podrías transmitir video o audio de esta manera; incluso podría generarse mientras lo procesas y lo envías. |
|||
|
|||
## Un `StreamingResponse` con `yield` { #a-streamingresponse-with-yield } |
|||
|
|||
Si declaras un `response_class=StreamingResponse` en tu *path operation function*, puedes usar `yield` para enviar cada bloque de datos a su vez. |
|||
|
|||
{* ../../docs_src/stream_data/tutorial001_py310.py ln[1:23] hl[20,23] *} |
|||
|
|||
FastAPI entregará cada bloque de datos a `StreamingResponse` tal cual, no intentará convertirlo a JSON ni nada parecido. |
|||
|
|||
### *path operation functions* no async { #non-async-path-operation-functions } |
|||
|
|||
También puedes usar funciones `def` normales (sin `async`) y usar `yield` de la misma manera. |
|||
|
|||
{* ../../docs_src/stream_data/tutorial001_py310.py ln[26:29] hl[27] *} |
|||
|
|||
### Sin anotación { #no-annotation } |
|||
|
|||
Realmente no necesitas declarar la anotación de tipo de retorno para transmitir datos binarios. |
|||
|
|||
Como FastAPI no intentará convertir los datos a JSON con Pydantic ni serializarlos de ninguna manera, en este caso la anotación de tipos es solo para que la use tu editor y tus herramientas; FastAPI no la usará. |
|||
|
|||
{* ../../docs_src/stream_data/tutorial001_py310.py ln[32:35] hl[33] *} |
|||
|
|||
Esto también significa que con `StreamingResponse` tienes la libertad y la responsabilidad de producir y codificar los bytes de datos exactamente como necesites enviarlos, independientemente de las anotaciones de tipos. 🤓 |
|||
|
|||
### Transmitir bytes { #stream-bytes } |
|||
|
|||
Uno de los casos de uso principales sería transmitir `bytes` en lugar de strings; por supuesto puedes hacerlo. |
|||
|
|||
{* ../../docs_src/stream_data/tutorial001_py310.py ln[44:47] hl[47] *} |
|||
|
|||
## Un `PNGStreamingResponse` personalizado { #a-custom-pngstreamingresponse } |
|||
|
|||
En los ejemplos anteriores, se transmitieron los bytes de datos, pero la response no tenía un header `Content-Type`, así que el cliente no sabía qué tipo de datos estaba recibiendo. |
|||
|
|||
Puedes crear una subclase personalizada de `StreamingResponse` que establezca el header `Content-Type` al tipo de datos que estás transmitiendo. |
|||
|
|||
Por ejemplo, puedes crear un `PNGStreamingResponse` que establezca el header `Content-Type` a `image/png` usando el atributo `media_type`: |
|||
|
|||
{* ../../docs_src/stream_data/tutorial002_py310.py ln[6,19:20] hl[20] *} |
|||
|
|||
Luego puedes usar esta nueva clase en `response_class=PNGStreamingResponse` en tu *path operation function*: |
|||
|
|||
{* ../../docs_src/stream_data/tutorial002_py310.py ln[23:27] hl[23] *} |
|||
|
|||
### Simular un archivo { #simulate-a-file } |
|||
|
|||
En este ejemplo estamos simulando un archivo con `io.BytesIO`, que es un objeto tipo archivo que vive solo en memoria, pero nos permite usar la misma interfaz. |
|||
|
|||
Por ejemplo, podemos iterarlo para consumir su contenido, como podríamos con un archivo. |
|||
|
|||
{* ../../docs_src/stream_data/tutorial002_py310.py ln[1:27] hl[3,12:13,25] *} |
|||
|
|||
/// note | Detalles técnicos |
|||
|
|||
Las otras dos variables, `image_base64` y `binary_image`, son una imagen codificada en Base64 y luego convertida a bytes, para después pasarla a `io.BytesIO`. |
|||
|
|||
Solo para que pueda vivir en el mismo archivo para este ejemplo y puedas copiarlo y ejecutarlo tal cual. 🥚 |
|||
|
|||
/// |
|||
|
|||
Al usar un bloque `with`, nos aseguramos de que el objeto tipo archivo se cierre cuando termine la función generadora (la función con `yield`). Es decir, después de que termine de enviar la response. |
|||
|
|||
No sería tan importante en este ejemplo específico porque es un archivo falso en memoria (con `io.BytesIO`), pero con un archivo real sí sería importante asegurarse de que el archivo se cierre al terminar de trabajar con él. |
|||
|
|||
### Archivos y async { #files-and-async } |
|||
|
|||
En la mayoría de los casos, los objetos tipo archivo no son compatibles con `async` y `await` por defecto. |
|||
|
|||
Por ejemplo, no tienen un `await file.read()`, ni un `async for chunk in file`. |
|||
|
|||
Y en muchos casos leerlos sería una operación bloqueante (que podría bloquear el event loop), porque se leen desde disco o desde la red. |
|||
|
|||
/// info | Información |
|||
|
|||
El ejemplo anterior es en realidad una excepción, porque el objeto `io.BytesIO` ya está en memoria, así que leerlo no bloqueará nada. |
|||
|
|||
Pero en muchos casos leer un archivo u objeto tipo archivo sí bloquearía. |
|||
|
|||
/// |
|||
|
|||
Para evitar bloquear el event loop, puedes simplemente declarar la *path operation function* con un `def` normal en lugar de `async def`; de esa forma FastAPI la ejecutará en un worker de threadpool para evitar bloquear el loop principal. |
|||
|
|||
{* ../../docs_src/stream_data/tutorial002_py310.py ln[30:34] hl[31] *} |
|||
|
|||
/// tip | Consejo |
|||
|
|||
Si necesitas llamar código bloqueante desde dentro de una función async, o una función async desde dentro de una función bloqueante, podrías usar [Asyncer](https://asyncer.tiangolo.com), un paquete hermano de FastAPI. |
|||
|
|||
/// |
|||
|
|||
### `yield from` { #yield-from } |
|||
|
|||
Cuando estés iterando sobre algo, como un objeto tipo archivo, y estés haciendo `yield` para cada elemento, también podrías usar `yield from` para hacer `yield` de cada elemento directamente y saltarte el `for`. |
|||
|
|||
Esto no es particular de FastAPI, es simplemente Python, pero es un truco útil que conviene conocer. 😎 |
|||
|
|||
{* ../../docs_src/stream_data/tutorial002_py310.py ln[37:40] hl[40] *} |
|||
@ -0,0 +1,88 @@ |
|||
# Chequeo estricto de Content-Type { #strict-content-type-checking } |
|||
|
|||
Por defecto, **FastAPI** usa un chequeo estricto del header `Content-Type` para request bodies JSON, esto significa que las requests JSON deben incluir un header `Content-Type` válido (p. ej. `application/json`) para que el request body se parse como JSON. |
|||
|
|||
## Riesgo de CSRF { #csrf-risk } |
|||
|
|||
Este comportamiento por defecto provee protección contra una clase de ataques de **Cross-Site Request Forgery (CSRF)** en un escenario muy específico. |
|||
|
|||
Estos ataques aprovechan que los navegadores permiten que los scripts envíen requests sin hacer un preflight de CORS cuando: |
|||
|
|||
* no tienen un header `Content-Type` (p. ej. usando `fetch()` con un body `Blob`) |
|||
* y no envían credenciales de autenticación. |
|||
|
|||
Este tipo de ataque es relevante principalmente cuando: |
|||
|
|||
* la aplicación corre localmente (p. ej. en `localhost`) o en una red interna |
|||
* y la aplicación no tiene ninguna autenticación, espera que cualquier request de la misma red sea confiable. |
|||
|
|||
## Ejemplo de ataque { #example-attack } |
|||
|
|||
Imagina que construyes una forma de ejecutar un agente de IA local. |
|||
|
|||
Provee un API en |
|||
|
|||
``` |
|||
http://localhost:8000/v1/agents/multivac |
|||
``` |
|||
|
|||
También hay un frontend en |
|||
|
|||
``` |
|||
http://localhost:8000 |
|||
``` |
|||
|
|||
/// tip | Consejo |
|||
|
|||
Ten en cuenta que ambos tienen el mismo host. |
|||
|
|||
/// |
|||
|
|||
Luego, usando el frontend, puedes hacer que el agente de IA haga cosas en tu nombre. |
|||
|
|||
Como está corriendo localmente y no en Internet abierta, decides no tener ninguna autenticación configurada, confiando simplemente en el acceso a la red local. |
|||
|
|||
Entonces, uno de tus usuarios podría instalarlo y ejecutarlo localmente. |
|||
|
|||
Después podría abrir un sitio web malicioso, por ejemplo algo como |
|||
|
|||
``` |
|||
https://evilhackers.example.com |
|||
``` |
|||
|
|||
Y ese sitio malicioso envía requests usando `fetch()` con un body `Blob` al API local en |
|||
|
|||
``` |
|||
http://localhost:8000/v1/agents/multivac |
|||
``` |
|||
|
|||
Aunque el host del sitio malicioso y el de la app local sea diferente, el navegador no disparará un preflight de CORS porque: |
|||
|
|||
* Está corriendo sin ninguna autenticación, no tiene que enviar credenciales. |
|||
* El navegador cree que no está enviando JSON (por la falta del header `Content-Type`). |
|||
|
|||
Entonces el sitio malicioso podría hacer que el agente de IA local envíe mensajes agresivos al exjefe del usuario... o peor. 😅 |
|||
|
|||
## Internet abierta { #open-internet } |
|||
|
|||
Si tu app está en Internet abierta, no “confiarías en la red” ni permitirías que cualquiera envíe requests privilegiadas sin autenticación. |
|||
|
|||
Los atacantes podrían simplemente ejecutar un script para enviar requests a tu API, sin necesidad de interacción del navegador, así que probablemente ya estás asegurando cualquier endpoint privilegiado. |
|||
|
|||
En ese caso, este ataque/riesgo no aplica a ti. |
|||
|
|||
Este riesgo y ataque es relevante principalmente cuando la app corre en la red local y esa es la única protección asumida. |
|||
|
|||
## Permitir requests sin Content-Type { #allowing-requests-without-content-type } |
|||
|
|||
Si necesitas soportar clientes que no envían un header `Content-Type`, puedes desactivar el chequeo estricto configurando `strict_content_type=False`: |
|||
|
|||
{* ../../docs_src/strict_content_type/tutorial001_py310.py hl[4] *} |
|||
|
|||
Con esta configuración, las requests sin un header `Content-Type` tendrán su body parseado como JSON, que es el mismo comportamiento de versiones anteriores de FastAPI. |
|||
|
|||
/// info | Información |
|||
|
|||
Este comportamiento y configuración se añadieron en FastAPI 0.132.0. |
|||
|
|||
/// |
|||
@ -0,0 +1,23 @@ |
|||
# Soporte del editor { #editor-support } |
|||
|
|||
La [Extensión de FastAPI](https://marketplace.visualstudio.com/items?itemName=FastAPILabs.fastapi-vscode) oficial mejora tu flujo de trabajo de desarrollo con FastAPI con descubrimiento de *path operation*, navegación, además de deployment a FastAPI Cloud y streaming en vivo de logs. |
|||
|
|||
Para más detalles sobre la extensión, consulta el README en el [repositorio de GitHub](https://github.com/fastapi/fastapi-vscode). |
|||
|
|||
## Configuración e instalación { #setup-and-installation } |
|||
|
|||
La **Extensión de FastAPI** está disponible tanto para [VS Code](https://code.visualstudio.com/) como para [Cursor](https://www.cursor.com/). Se puede instalar directamente desde el panel de Extensiones en cada editor buscando "FastAPI" y seleccionando la extensión publicada por **FastAPI Labs**. La extensión también funciona en editores basados en navegador como [vscode.dev](https://vscode.dev) y [github.dev](https://github.dev). |
|||
|
|||
### Descubrimiento de la aplicación { #application-discovery } |
|||
|
|||
Por defecto, la extensión descubrirá automáticamente aplicaciones FastAPI en tu espacio de trabajo escaneando archivos que creen un instance de `FastAPI()`. Si la detección automática no funciona con la estructura de tu proyecto, puedes especificar un punto de entrada mediante `[tool.fastapi]` en `pyproject.toml` o la configuración de VS Code `fastapi.entryPoint` usando notación de módulo (p. ej. `myapp.main:app`). |
|||
|
|||
## Funcionalidades { #features } |
|||
|
|||
- **Explorador de Path Operations** - Una vista en árbol en la barra lateral de todas las <dfn title="rutas, endpoints">*path operations*</dfn> de tu aplicación. Haz clic para saltar a cualquier definición de ruta o de router. |
|||
- **Búsqueda de rutas** - Busca por path, método o nombre con <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>E</kbd> (en macOS: <kbd>Cmd</kbd> + <kbd>Shift</kbd> + <kbd>E</kbd>). |
|||
- **Navegación con CodeLens** - Enlaces clicables encima de llamadas del cliente de tests (p. ej. `client.get('/items')`) que saltan a la *path operation* correspondiente para navegar rápidamente entre tests e implementación. |
|||
- **Desplegar en FastAPI Cloud** - Deployment con un clic de tu app a [FastAPI Cloud](https://fastapicloud.com/). |
|||
- **Streaming de logs de la aplicación** - Streaming en tiempo real de logs desde tu aplicación desplegada en FastAPI Cloud, con filtrado por nivel y búsqueda de texto. |
|||
|
|||
Si quieres familiarizarte con las funcionalidades de la extensión, puedes revisar el recorrido guiado de la extensión abriendo la Paleta de Comandos (<kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>P</kbd> o en macOS: <kbd>Cmd</kbd> + <kbd>Shift</kbd> + <kbd>P</kbd>) y seleccionando "Welcome: Open walkthrough..." y luego eligiendo el recorrido "Get started with FastAPI". |
|||
@ -0,0 +1,120 @@ |
|||
# Server-Sent Events (SSE) { #server-sent-events-sse } |
|||
|
|||
Puedes enviar datos en streaming al cliente usando **Server-Sent Events** (SSE). |
|||
|
|||
Esto es similar a [Stream JSON Lines](stream-json-lines.md), pero usa el formato `text/event-stream`, que los navegadores soportan de forma nativa con la [`EventSource` API](https://developer.mozilla.org/en-US/docs/Web/API/EventSource). |
|||
|
|||
/// info | Información |
|||
|
|||
Añadido en FastAPI 0.135.0. |
|||
|
|||
/// |
|||
|
|||
## ¿Qué son los Server-Sent Events? { #what-are-server-sent-events } |
|||
|
|||
SSE es un estándar para hacer streaming de datos desde el servidor al cliente sobre HTTP. |
|||
|
|||
Cada evento es un pequeño bloque de texto con “campos” como `data`, `event`, `id` y `retry`, separados por líneas en blanco. |
|||
|
|||
Se ve así: |
|||
|
|||
``` |
|||
data: {"name": "Portal Gun", "price": 999.99} |
|||
|
|||
data: {"name": "Plumbus", "price": 32.99} |
|||
|
|||
``` |
|||
|
|||
SSE se usa comúnmente para streaming de chat de IA, notificaciones en vivo, logs y observabilidad, y otros casos donde el servidor envía actualizaciones al cliente. |
|||
|
|||
/// tip | Consejo |
|||
|
|||
Si quieres hacer streaming de datos binarios, por ejemplo video o audio, Revisa la guía avanzada: [Stream Data](../advanced/stream-data.md). |
|||
|
|||
/// |
|||
|
|||
## Streaming de SSE con FastAPI { #stream-sse-with-fastapi } |
|||
|
|||
Para hacer streaming de SSE con FastAPI, usa `yield` en tu path operation function y establece `response_class=EventSourceResponse`. |
|||
|
|||
import `EventSourceResponse` de `fastapi.sse`: |
|||
|
|||
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[1:25] hl[4,22] *} |
|||
|
|||
Cada ítem producido con `yield` se codifica como JSON y se envía en el campo `data:` de un evento SSE. |
|||
|
|||
Si declaras el tipo de retorno como `AsyncIterable[Item]`, FastAPI lo usará para **validar**, **documentar** y **serializar** los datos usando Pydantic. |
|||
|
|||
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[1:25] hl[10:12,23] *} |
|||
|
|||
/// tip | Consejo |
|||
|
|||
Como Pydantic lo serializará en el lado de **Rust**, obtendrás un **rendimiento** mucho mayor que si no declaras un tipo de retorno. |
|||
|
|||
/// |
|||
|
|||
### No async *path operation functions* { #non-async-path-operation-functions } |
|||
|
|||
También puedes usar funciones `def` normales (sin `async`), y usar `yield` de la misma manera. |
|||
|
|||
FastAPI se asegurará de ejecutarlo correctamente para que no bloquee el event loop. |
|||
|
|||
Como en este caso la función no es async, el tipo de retorno correcto sería `Iterable[Item]`: |
|||
|
|||
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[28:31] hl[29] *} |
|||
|
|||
### Sin tipo de retorno { #no-return-type } |
|||
|
|||
También puedes omitir el tipo de retorno. FastAPI usará el [`jsonable_encoder`](./encoder.md) para convertir los datos y enviarlos. |
|||
|
|||
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[34:37] hl[35] *} |
|||
|
|||
## `ServerSentEvent` { #serversentevent } |
|||
|
|||
Si necesitas configurar campos SSE como `event`, `id`, `retry` o `comment`, puedes hacer `yield` de objetos `ServerSentEvent` en lugar de datos simples. |
|||
|
|||
import `ServerSentEvent` de `fastapi.sse`: |
|||
|
|||
{* ../../docs_src/server_sent_events/tutorial002_py310.py hl[4,26] *} |
|||
|
|||
El campo `data` siempre se codifica como JSON. Puedes pasar cualquier valor que pueda serializarse como JSON, incluidos modelos de Pydantic. |
|||
|
|||
## Datos sin procesar { #raw-data } |
|||
|
|||
Si necesitas enviar datos **sin** codificarlos a JSON, usa `raw_data` en lugar de `data`. |
|||
|
|||
Esto es útil para enviar texto preformateado, líneas de log, o valores especiales de <dfn title="Un valor usado para indicar una condición o estado especial">"centinela"</dfn> como `[DONE]`. |
|||
|
|||
{* ../../docs_src/server_sent_events/tutorial003_py310.py hl[17] *} |
|||
|
|||
/// note | Nota |
|||
|
|||
`data` y `raw_data` son mutuamente excluyentes. Solo puedes establecer uno de ellos en cada `ServerSentEvent`. |
|||
|
|||
/// |
|||
|
|||
## Reanudar con `Last-Event-ID` { #resuming-with-last-event-id } |
|||
|
|||
Cuando un navegador se reconecta después de una caída de la conexión, envía el último `id` recibido en el header `Last-Event-ID`. |
|||
|
|||
Puedes leerlo como un parámetro de header y usarlo para reanudar el stream desde donde el cliente se quedó: |
|||
|
|||
{* ../../docs_src/server_sent_events/tutorial004_py310.py hl[25,27,31] *} |
|||
|
|||
## SSE con `POST` { #sse-with-post } |
|||
|
|||
SSE funciona con **cualquier método HTTP**, no solo con `GET`. |
|||
|
|||
Esto es útil para protocolos como [MCP](https://modelcontextprotocol.io) que hacen streaming de SSE sobre `POST`: |
|||
|
|||
{* ../../docs_src/server_sent_events/tutorial005_py310.py hl[14] *} |
|||
|
|||
## Detalles técnicos { #technical-details } |
|||
|
|||
FastAPI implementa algunas mejores prácticas de SSE desde el primer momento. |
|||
|
|||
- Enviar un comentario de **"keep alive" `ping`** cada 15 segundos cuando no ha habido ningún mensaje, para evitar que algunos proxies cierren la conexión, como se sugiere en la [Especificación HTML: Server-Sent Events](https://html.spec.whatwg.org/multipage/server-sent-events.html#authoring-notes). |
|||
- Configurar el header `Cache-Control: no-cache` para **evitar el almacenamiento en caché** del stream. |
|||
- Configurar un header especial `X-Accel-Buffering: no` para **evitar el buffering** en algunos proxies como Nginx. |
|||
|
|||
No tienes que hacer nada, funciona tal cual viene. 🤓 |
|||
@ -0,0 +1,111 @@ |
|||
# Transmitir JSON Lines { #stream-json-lines } |
|||
|
|||
Podrías tener una secuencia de datos que quieras enviar en un "**stream**", podrías hacerlo con **JSON Lines**. |
|||
|
|||
/// info | Información |
|||
|
|||
Añadido en FastAPI 0.134.0. |
|||
|
|||
/// |
|||
|
|||
## ¿Qué es un Stream? { #what-is-a-stream } |
|||
|
|||
Hacer "**Streaming**" de datos significa que tu app empezará a enviar ítems de datos al cliente sin esperar a que toda la secuencia de ítems esté lista. |
|||
|
|||
Entonces, enviará el primer ítem, el cliente lo recibirá y empezará a procesarlo, y tú podrías seguir produciendo el siguiente ítem. |
|||
|
|||
```mermaid |
|||
sequenceDiagram |
|||
participant App |
|||
participant Client |
|||
|
|||
App->>App: Produce Item 1 |
|||
App->>Client: Send Item 1 |
|||
App->>App: Produce Item 2 |
|||
Client->>Client: Process Item 1 |
|||
App->>Client: Send Item 2 |
|||
App->>App: Produce Item 3 |
|||
Client->>Client: Process Item 2 |
|||
App->>Client: Send Item 3 |
|||
Client->>Client: Process Item 3 |
|||
Note over App: Keeps producing... |
|||
Note over Client: Keeps consuming... |
|||
``` |
|||
|
|||
Incluso podría ser un stream infinito, donde sigues enviando datos. |
|||
|
|||
## JSON Lines { #json-lines } |
|||
|
|||
En estos casos, es común enviar "**JSON Lines**", que es un formato donde envías un objeto JSON por línea. |
|||
|
|||
Una response tendría un tipo de contenido `application/jsonl` (en lugar de `application/json`) y el response body sería algo como: |
|||
|
|||
```json |
|||
{"name": "Plumbus", "description": "A multi-purpose household device."} |
|||
{"name": "Portal Gun", "description": "A portal opening device."} |
|||
{"name": "Meeseeks Box", "description": "A box that summons a Meeseeks."} |
|||
``` |
|||
|
|||
Es muy similar a un array JSON (equivalente de una list de Python), pero en lugar de estar envuelto en `[]` y tener `,` entre los ítems, tiene **un objeto JSON por línea**, separados por un carácter de nueva línea. |
|||
|
|||
/// info | Información |
|||
|
|||
El punto importante es que tu app podrá producir cada línea a su turno, mientras el cliente consume las líneas anteriores. |
|||
|
|||
/// |
|||
|
|||
/// note | Detalles técnicos |
|||
|
|||
Como cada objeto JSON estará separado por una nueva línea, no pueden contener caracteres de nueva línea literales en su contenido, pero sí pueden contener nuevas líneas escapadas (`\n`), lo cual es parte del estándar JSON. |
|||
|
|||
Pero normalmente no tendrás que preocuparte por eso, se hace automáticamente, sigue leyendo. 🤓 |
|||
|
|||
/// |
|||
|
|||
## Casos de uso { #use-cases } |
|||
|
|||
Podrías usar esto para hacer stream de datos desde un servicio de **AI LLM**, desde **logs** o **telemetry**, o desde otros tipos de datos que puedan estructurarse en ítems **JSON**. |
|||
|
|||
/// tip | Consejo |
|||
|
|||
Si quieres hacer stream de datos binarios, por ejemplo video o audio, Revisa la guía avanzada: [Transmitir datos](../advanced/stream-data.md). |
|||
|
|||
/// |
|||
|
|||
## Transmitir JSON Lines con FastAPI { #stream-json-lines-with-fastapi } |
|||
|
|||
Para transmitir JSON Lines con FastAPI puedes, en lugar de usar `return` en tu *path operation function*, usar `yield` para producir cada ítem a su turno. |
|||
|
|||
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[1:24] hl[24] *} |
|||
|
|||
Si cada ítem JSON que quieres enviar de vuelta es de tipo `Item` (un modelo de Pydantic) y es una función async, puedes declarar el tipo de retorno como `AsyncIterable[Item]`: |
|||
|
|||
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[1:24] hl[9:11,22] *} |
|||
|
|||
Si declaras el tipo de retorno, FastAPI lo usará para **validar** los datos, **documentarlos** en OpenAPI, **filtrarlos** y **serializarlos** usando Pydantic. |
|||
|
|||
/// tip | Consejo |
|||
|
|||
Como Pydantic lo serializará en el lado de **Rust**, obtendrás un **rendimiento** mucho mayor que si no declaras un tipo de retorno. |
|||
|
|||
/// |
|||
|
|||
### *path operation functions* no-async { #non-async-path-operation-functions } |
|||
|
|||
También puedes usar funciones `def` regulares (sin `async`), y usar `yield` de la misma forma. |
|||
|
|||
FastAPI se asegurará de que se ejecute correctamente para que no bloquee el event loop. |
|||
|
|||
Como en este caso la función no es async, el tipo de retorno correcto sería `Iterable[Item]`: |
|||
|
|||
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[27:30] hl[28] *} |
|||
|
|||
### Sin tipo de retorno { #no-return-type } |
|||
|
|||
También puedes omitir el tipo de retorno. Entonces FastAPI usará [`jsonable_encoder`](./encoder.md) para convertir los datos a algo que se pueda serializar a JSON y luego enviarlo como JSON Lines. |
|||
|
|||
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[33:36] hl[34] *} |
|||
|
|||
## Server-Sent Events (SSE) { #server-sent-events-sse } |
|||
|
|||
FastAPI también tiene soporte de primera clase para Server-Sent Events (SSE), que son bastante similares pero con un par de detalles extra. Puedes aprender sobre ellos en el siguiente capítulo: [Eventos enviados por el servidor (SSE)](server-sent-events.md). 🤓 |
|||
Loading…
Reference in new issue