mirror of https://github.com/tiangolo/fastapi.git
🌐 Update translations for es (add-missing) (#15154)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
parent
b61bdb79b8
commit
cd5869b897
|
|
@ -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