Podrías crear una API con una path operation que podría disparar una request a una API externa creada por alguien más (probablemente el mismo desarrollador que estaría usando tu API).
El proceso que ocurre cuando tu app de API llama a la API externa se llama "callback". Porque el software que el desarrollador externo escribió envía una request a tu API y luego tu API llama de vuelta, enviando una request a una API externa (que probablemente fue creada por el mismo desarrollador).
En este caso, podrías querer documentar cómo debería verse esa API externa. Qué path operation debería tener, qué body debería esperar, qué response debería retornar, etc.
Veamos primero cómo se vería la app normal de la API antes de añadir el callback.
Tendrá una path operation que recibirá un body Invoice, y un query parameter callback_url que contendrá la URL para el callback.
Esta parte es bastante normal, la mayoría del código probablemente ya te sea familiar:
fromfastapiimportAPIRouter,FastAPIfrompydanticimportBaseModel,HttpUrlapp=FastAPI()classInvoice(BaseModel):id:strtitle:str|None=Nonecustomer:strtotal:floatclassInvoiceEvent(BaseModel):description:strpaid:boolclassInvoiceEventReceived(BaseModel):ok:boolinvoices_callback_router=APIRouter()@invoices_callback_router.post("{$callback_url}/invoices/{$request.body.id}",response_model=InvoiceEventReceived)definvoice_notification(body:InvoiceEvent):pass@app.post("/invoices/",callbacks=invoices_callback_router.routes)defcreate_invoice(invoice:Invoice,callback_url:HttpUrl|None=None):""" Create an invoice. This will (let's imagine) let the API user (some external developer) create an invoice. And this path operation will: * Send the invoice to the client. * Collect the money from the client. * Send a notification back to the API user (the external developer), as a callback. * At this point is that the API will somehow send a POST request to the external API with the notification of the invoice event (e.g. "payment successful"). """# Send the invoice, collect the money, send the notification (the callback)return{"msg":"Invoice received"}
Consejo
El query parameter callback_url usa un tipo Url de Pydantic.
Lo único nuevo es callbacks=invoices_callback_router.routes como argumento del decorador de la path operation. Veremos qué es eso a continuación.
Pero posiblemente la parte más importante del callback es asegurar que el usuario de tu API (el desarrollador externo) implemente la API externa correctamente, según los datos que tu API va a enviar en el body de la request del callback, etc.
Así que, lo que haremos a continuación es añadir el código para documentar cómo debería verse esa API externa para recibir el callback de tu API.
Esa documentación aparecerá en el Swagger UI en /docs de tu API, y les informará a los desarrolladores externos cómo construir la API externa.
Este ejemplo no implementa el callback en sí (que podría ser solo una línea de código), solo la parte de documentación.
Consejo
El callback real es solo una request HTTP.
Al implementar el callback tú mismo, podrías usar algo como HTTPX o Requests.
Este código no se ejecutará en tu app, solo lo necesitamos para documentar cómo debería verse esa API externa.
Pero ya sabes cómo crear fácilmente documentación automática para una API con FastAPI.
Así que vamos a usar ese mismo conocimiento para documentar cómo debería verse la API externa... creando las path operation(s) que la API externa debería implementar (las que tu API llamará).
Consejo
Al escribir el código para documentar un callback, puede ser útil imaginar que eres ese desarrollador externo. Y que estás actualmente implementando la API externa, no tu API.
Adoptar temporalmente este punto de vista (del desarrollador externo) puede ayudarte a sentir que es más obvio dónde poner los parámetros, el modelo de Pydantic para el body, para la response, etc. de esa API externa.
Primero crea un nuevo APIRouter que contendrá uno o más callbacks.
fromfastapiimportAPIRouter,FastAPIfrompydanticimportBaseModel,HttpUrlapp=FastAPI()classInvoice(BaseModel):id:strtitle:str|None=Nonecustomer:strtotal:floatclassInvoiceEvent(BaseModel):description:strpaid:boolclassInvoiceEventReceived(BaseModel):ok:boolinvoices_callback_router=APIRouter()@invoices_callback_router.post("{$callback_url}/invoices/{$request.body.id}",response_model=InvoiceEventReceived)definvoice_notification(body:InvoiceEvent):pass@app.post("/invoices/",callbacks=invoices_callback_router.routes)defcreate_invoice(invoice:Invoice,callback_url:HttpUrl|None=None):""" Create an invoice. This will (let's imagine) let the API user (some external developer) create an invoice. And this path operation will: * Send the invoice to the client. * Collect the money from the client. * Send a notification back to the API user (the external developer), as a callback. * At this point is that the API will somehow send a POST request to the external API with the notification of the invoice event (e.g. "payment successful"). """# Send the invoice, collect the money, send the notification (the callback)return{"msg":"Invoice received"}
Para crear la path operation del callback usa el mismo APIRouter que creaste arriba.
Debería verse igual que una path operation normal de FastAPI:
Probablemente debería tener una declaración del body que debería recibir, por ejemplo body: InvoiceEvent.
Y también podría tener una declaración de la response que debería retornar, por ejemplo response_model=InvoiceEventReceived.
fromfastapiimportAPIRouter,FastAPIfrompydanticimportBaseModel,HttpUrlapp=FastAPI()classInvoice(BaseModel):id:strtitle:str|None=Nonecustomer:strtotal:floatclassInvoiceEvent(BaseModel):description:strpaid:boolclassInvoiceEventReceived(BaseModel):ok:boolinvoices_callback_router=APIRouter()@invoices_callback_router.post("{$callback_url}/invoices/{$request.body.id}",response_model=InvoiceEventReceived)definvoice_notification(body:InvoiceEvent):pass@app.post("/invoices/",callbacks=invoices_callback_router.routes)defcreate_invoice(invoice:Invoice,callback_url:HttpUrl|None=None):""" Create an invoice. This will (let's imagine) let the API user (some external developer) create an invoice. And this path operation will: * Send the invoice to the client. * Collect the money from the client. * Send a notification back to the API user (the external developer), as a callback. * At this point is that the API will somehow send a POST request to the external API with the notification of the invoice event (e.g. "payment successful"). """# Send the invoice, collect the money, send the notification (the callback)return{"msg":"Invoice received"}
Hay 2 diferencias principales con una path operation normal:
No necesita tener ningún código real, porque tu app nunca llamará este código. Solo se usa para documentar la API externa. Así que, la función podría simplemente tener pass.
El path puede contener una expresión de OpenAPI 3 (ver más abajo) donde puede usar variables con parámetros y partes de la request original enviada a tu API.
y esperaría una response de esa API externa con un body JSON como:
{"ok":true}
Consejo
Notá cómo la URL del callback usada contiene la URL recibida como query parameter en callback_url (https://www.external.org/events) y también el id de la factura desde dentro del body JSON (2expen51ve).
En este punto tienes las callback path operation(s) necesarias (las que el desarrollador externo debería implementar en la API externa) en el router de callback que creaste arriba.
Ahora usa el parámetro callbacks en el decorador de path operation de tu API para pasar el atributo .routes de ese router de callback:
fromfastapiimportAPIRouter,FastAPIfrompydanticimportBaseModel,HttpUrlapp=FastAPI()classInvoice(BaseModel):id:strtitle:str|None=Nonecustomer:strtotal:floatclassInvoiceEvent(BaseModel):description:strpaid:boolclassInvoiceEventReceived(BaseModel):ok:boolinvoices_callback_router=APIRouter()@invoices_callback_router.post("{$callback_url}/invoices/{$request.body.id}",response_model=InvoiceEventReceived)definvoice_notification(body:InvoiceEvent):pass@app.post("/invoices/",callbacks=invoices_callback_router.routes)defcreate_invoice(invoice:Invoice,callback_url:HttpUrl|None=None):""" Create an invoice. This will (let's imagine) let the API user (some external developer) create an invoice. And this path operation will: * Send the invoice to the client. * Collect the money from the client. * Send a notification back to the API user (the external developer), as a callback. * At this point is that the API will somehow send a POST request to the external API with the notification of the invoice event (e.g. "payment successful"). """# Send the invoice, collect the money, send the notification (the callback)return{"msg":"Invoice received"}
Consejo
Notá que no estás pasando el router mismo (invoices_callback_router) a callbacks=, sino sus .routes, como en invoices_callback_router.routes. FastAPI usará esas routes para generar la documentación de OpenAPI del callback.