Callbacks OpenAPI¶
đ Traduction par IA et humains
Cette traduction a Ă©tĂ© rĂ©alisĂ©e par une IA guidĂ©e par des humains. đ€
Elle peut contenir des erreurs d'interprĂ©tation du sens original, ou paraĂźtre peu naturelle, etc. đ€
Vous pouvez améliorer cette traduction en nous aidant à mieux guider le LLM d'IA.
Vous pourriez crĂ©er une API avec un chemin d'accĂšs qui dĂ©clenche une requĂȘte vers une API externe créée par quelqu'un d'autre (probablement la mĂȘme personne dĂ©veloppeuse qui utiliserait votre API).
Le processus qui se produit lorsque votre application API appelle lâAPI externe sâappelle un « callback ». Parce que le logiciel Ă©crit par la personne dĂ©veloppeuse externe envoie une requĂȘte Ă votre API puis votre API « rappelle », en envoyant une requĂȘte Ă une API externe (probablement créée par la mĂȘme personne dĂ©veloppeuse).
Dans ce cas, vous pourriez vouloir documenter à quoi cette API externe devrait ressembler. Quel chemin d'accÚs elle devrait avoir, quel corps elle devrait attendre, quelle réponse elle devrait renvoyer, etc.
Une application avec des callbacks¶
Voyons tout cela avec un exemple.
Imaginez que vous développiez une application qui permet de créer des factures.
Ces factures auront un id, un title (facultatif), un customer et un total.
Lâutilisateur de votre API (une personne dĂ©veloppeuse externe) crĂ©era une facture dans votre API avec une requĂȘte POST.
Ensuite votre API va (imaginons) :
- Envoyer la facture à un client de la personne développeuse externe.
- Encaisser lâargent.
- Renvoyer une notification Ă lâutilisateur de lâAPI (la personne dĂ©veloppeuse externe).
- Cela sera fait en envoyant une requĂȘte POST (depuis votre API) vers une API externe fournie par cette personne dĂ©veloppeuse externe (câest le « callback »).
Lâapplication FastAPI normale¶
Voyons dâabord Ă quoi ressemble lâapplication API normale avant dâajouter le callback.
Elle aura un chemin d'accĂšs qui recevra un corps Invoice, et un paramĂštre de requĂȘte callback_url qui contiendra lâURL pour le callback.
Cette partie est assez normale, la plupart du code vous est probablement déjà familier :
from fastapi import APIRouter, FastAPI
from pydantic import BaseModel, HttpUrl
app = FastAPI()
class Invoice(BaseModel):
id: str
title: str | None = None
customer: str
total: float
class InvoiceEvent(BaseModel):
description: str
paid: bool
class InvoiceEventReceived(BaseModel):
ok: bool
invoices_callback_router = APIRouter()
@invoices_callback_router.post(
"{$callback_url}/invoices/{$request.body.id}", response_model=InvoiceEventReceived
)
def invoice_notification(body: InvoiceEvent):
pass
@app.post("/invoices/", callbacks=invoices_callback_router.routes)
def create_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"}
Astuce
Le paramĂštre de requĂȘte callback_url utilise un type Pydantic Url.
La seule nouveautĂ© est callbacks=invoices_callback_router.routes comme argument du dĂ©corateur de chemin d'accĂšs. Nous allons voir ce que câest ensuite.
Documenter le callback¶
Le code réel du callback dépendra fortement de votre application API.
Et il variera probablement beaucoup dâune application Ă lâautre.
Cela pourrait ĂȘtre seulement une ou deux lignes de code, comme :
callback_url = "https://example.com/api/v1/invoices/events/"
httpx.post(callback_url, json={"description": "Invoice paid", "paid": True})
Mais la partie la plus importante du callback est sans doute de vous assurer que lâutilisateur de votre API (la personne dĂ©veloppeuse externe) implĂ©mente correctement lâAPI externe, conformĂ©ment aux donnĂ©es que votre API va envoyer dans le corps de la requĂȘte du callback, etc.
Ainsi, ce que nous allons faire ensuite, câest ajouter le code pour documenter Ă quoi cette API externe devrait ressembler pour recevoir le callback de votre API.
Cette documentation apparaĂźtra dans Swagger UI Ă /docs dans votre API, et permettra aux personnes dĂ©veloppeuses externes de savoir comment construire lâAPI externe.
Cet exemple nâimplĂ©mente pas le callback lui-mĂȘme (qui pourrait ĂȘtre une simple ligne de code), uniquement la partie documentation.
Astuce
Le callback rĂ©el nâest quâune requĂȘte HTTP.
En implĂ©mentant vous-mĂȘme le callback, vous pourriez utiliser quelque chose comme HTTPX ou Requests.
Ăcrire le code de documentation du callback¶
Ce code ne sera pas exécuté dans votre application, nous en avons seulement besoin pour documenter à quoi devrait ressembler cette API externe.
Mais vous savez déjà comment créer facilement une documentation automatique pour une API avec FastAPI.
Nous allons donc utiliser ce mĂȘme savoir pour documenter Ă quoi lâAPI externe devrait ressembler ... en crĂ©ant le(s) chemin(s) d'accĂšs que lâAPI externe devrait implĂ©menter (ceux que votre API appellera).
Astuce
Lorsque vous Ă©crivez le code pour documenter un callback, il peut ĂȘtre utile dâimaginer que vous ĂȘtes cette personne dĂ©veloppeuse externe. Et que vous implĂ©mentez actuellement lâAPI externe, pas votre API.
Adopter temporairement ce point de vue (celui de la personne dĂ©veloppeuse externe) peut vous aider Ă trouver plus Ă©vident oĂč placer les paramĂštres, le modĂšle Pydantic pour le corps, pour la rĂ©ponse, etc., pour cette API externe.
CrĂ©er un APIRouter de callback¶
Commencez par créer un nouveau APIRouter qui contiendra un ou plusieurs callbacks.
from fastapi import APIRouter, FastAPI
from pydantic import BaseModel, HttpUrl
app = FastAPI()
class Invoice(BaseModel):
id: str
title: str | None = None
customer: str
total: float
class InvoiceEvent(BaseModel):
description: str
paid: bool
class InvoiceEventReceived(BaseModel):
ok: bool
invoices_callback_router = APIRouter()
@invoices_callback_router.post(
"{$callback_url}/invoices/{$request.body.id}", response_model=InvoiceEventReceived
)
def invoice_notification(body: InvoiceEvent):
pass
@app.post("/invoices/", callbacks=invoices_callback_router.routes)
def create_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"}
CrĂ©er le chemin d'accĂšs du callback¶
Pour crĂ©er le chemin d'accĂšs du callback, utilisez le mĂȘme APIRouter que vous avez créé ci-dessus.
Il devrait ressembler exactement à un chemin d'accÚs FastAPI normal :
- Il devrait probablement dĂ©clarer le corps quâil doit recevoir, par exemple
body: InvoiceEvent. - Et il pourrait aussi dĂ©clarer la rĂ©ponse quâil doit renvoyer, par exemple
response_model=InvoiceEventReceived.
from fastapi import APIRouter, FastAPI
from pydantic import BaseModel, HttpUrl
app = FastAPI()
class Invoice(BaseModel):
id: str
title: str | None = None
customer: str
total: float
class InvoiceEvent(BaseModel):
description: str
paid: bool
class InvoiceEventReceived(BaseModel):
ok: bool
invoices_callback_router = APIRouter()
@invoices_callback_router.post(
"{$callback_url}/invoices/{$request.body.id}", response_model=InvoiceEventReceived
)
def invoice_notification(body: InvoiceEvent):
pass
@app.post("/invoices/", callbacks=invoices_callback_router.routes)
def create_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"}
Il y a 2 principales différences par rapport à un chemin d'accÚs normal :
- Il nâa pas besoin dâavoir de code rĂ©el, car votre application nâappellera jamais ce code. Il sert uniquement Ă documenter lâAPI externe. La fonction peut donc simplement contenir
pass. - Le chemin peut contenir une expression OpenAPI 3 (voir plus bas) oĂč il peut utiliser des variables avec des paramĂštres et des parties de la requĂȘte originale envoyĂ©e Ă votre API.
Lâexpression du chemin de callback¶
Le chemin du callback peut contenir une expression OpenAPI 3 qui peut inclure des parties de la requĂȘte originale envoyĂ©e Ă votre API.
Dans ce cas, câest la str :
"{$callback_url}/invoices/{$request.body.id}"
Ainsi, si lâutilisateur de votre API (la personne dĂ©veloppeuse externe) envoie une requĂȘte Ă votre API vers :
https://yourapi.com/invoices/?callback_url=https://www.external.org/events
avec un corps JSONÂ :
{
"id": "2expen51ve",
"customer": "Mr. Richie Rich",
"total": "9999"
}
alors votre API traitera la facture et, Ă un moment ultĂ©rieur, enverra une requĂȘte de callback Ă callback_url (lâAPI externe) :
https://www.external.org/events/invoices/2expen51ve
avec un corps JSON contenant quelque chose comme :
{
"description": "Payment celebration",
"paid": true
}
et elle sâattendra Ă une rĂ©ponse de cette API externe avec un corps JSON comme :
{
"ok": true
}
Astuce
Remarquez que lâURL de callback utilisĂ©e contient lâURL reçue en paramĂštre de requĂȘte dans callback_url (https://www.external.org/events) et aussi lâid de la facture Ă lâintĂ©rieur du corps JSON (2expen51ve).
Ajouter le routeur de callback¶
Ă ce stade, vous avez le(s) chemin(s) d'accĂšs de callback nĂ©cessaire(s) (celui/ceux que la personne dĂ©veloppeuse externe doit implĂ©menter dans lâAPI externe) dans le routeur de callback que vous avez créé ci-dessus.
Utilisez maintenant le paramĂštre callbacks dans le dĂ©corateur de chemin d'accĂšs de votre API pour passer lâattribut .routes (qui est en fait juste une list de routes/chemins d'accĂšs) depuis ce routeur de callback :
from fastapi import APIRouter, FastAPI
from pydantic import BaseModel, HttpUrl
app = FastAPI()
class Invoice(BaseModel):
id: str
title: str | None = None
customer: str
total: float
class InvoiceEvent(BaseModel):
description: str
paid: bool
class InvoiceEventReceived(BaseModel):
ok: bool
invoices_callback_router = APIRouter()
@invoices_callback_router.post(
"{$callback_url}/invoices/{$request.body.id}", response_model=InvoiceEventReceived
)
def invoice_notification(body: InvoiceEvent):
pass
@app.post("/invoices/", callbacks=invoices_callback_router.routes)
def create_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"}
Astuce
Remarquez que vous ne passez pas le routeur lui-mĂȘme (invoices_callback_router) Ă callback=, mais lâattribut .routes, comme dans invoices_callback_router.routes.
VĂ©rifier la documentation¶
Vous pouvez maintenant démarrer votre application et aller sur http://127.0.0.1:8000/docs.
Vous verrez votre documentation incluant une section « Callbacks » pour votre chemin d'accĂšs qui montre Ă quoi lâAPI externe devrait ressembler :
