# Зворотні виклики OpenAPI { #openapi-callbacks }

Ви можете створити API з операцією шляху, яка ініціюватиме запит до зовнішнього API, створеного кимось іншим (ймовірно тим самим розробником, який буде використовувати ваш API).

Процес, що відбувається, коли ваш застосунок API викликає зовнішній API, називається «зворотний виклик». Тому що програмне забезпечення, написане зовнішнім розробником, надсилає запит до вашого API, а потім ваш API виконує зворотний виклик, надсилаючи запит до зовнішнього API (його, ймовірно, також створив той самий розробник).

У такому випадку вам може знадобитися задокументувати, яким має бути той зовнішній API: які операції шляху він має мати, яке тіло очікувати, яку відповідь повертати тощо.

## Застосунок зі зворотними викликами { #an-app-with-callbacks }

Розгляньмо це на прикладі.

Уявімо, що ви розробляєте застосунок для створення рахунків.

Ці рахунки матимуть `id`, `title` (необов'язково), `customer` і `total`.

Користувач вашого API (зовнішній розробник) створить рахунок у вашому API за допомогою POST-запиту.

Потім ваш API буде (уявімо):

- Надсилати рахунок деякому клієнту зовнішнього розробника.
- Отримувати оплату.
- Надсилати сповіщення назад користувачу API (зовнішньому розробнику).
  - Це буде зроблено шляхом надсилання POST-запиту (з вашого API) до деякого зовнішнього API, наданого тим зовнішнім розробником (це і є «зворотний виклик»).

## Звичайний застосунок FastAPI { #the-normal-fastapi-app }

Спочатку подивімося, як виглядав би звичайний застосунок API до додавання зворотного виклику.

Він матиме операцію шляху, яка отримуватиме тіло `Invoice`, і параметр запиту `callback_url`, що міститиме URL для зворотного виклику.

Ця частина цілком звична, більшість коду вам, ймовірно, уже знайома:

{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[7:11,34:51] *}

/// tip | Порада

Параметр запиту `callback_url` використовує тип Pydantic [Url](https://docs.pydantic.dev/latest/api/networks/).

///

Єдина нова річ - це `callbacks=invoices_callback_router.routes` як аргумент декоратора операції шляху. Далі розглянемо, що це таке.

## Документування зворотного виклику { #documenting-the-callback }

Фактичний код зворотного виклику сильно залежатиме від вашого застосунку API.

І, ймовірно, сильно відрізнятиметься від застосунку до застосунку.

Це можуть бути лише один-два рядки коду, наприклад:

```Python
callback_url = "https://example.com/api/v1/invoices/events/"
httpx.post(callback_url, json={"description": "Invoice paid", "paid": True})
```

Але, можливо, найважливіша частина зворотного виклику - переконатися, що користувач вашого API (зовнішній розробник) правильно реалізує зовнішній API відповідно до даних, які ваш API надсилатиме в тілі запиту зворотного виклику тощо.

Тому далі ми додамо код, щоб задокументувати, яким має бути цей зовнішній API, щоб приймати зворотний виклик від вашого API.

Ця документація з'явиться в Swagger UI за адресою `/docs` у вашому API і дасть змогу зовнішнім розробникам зрозуміти, як створити зовнішній API.

У цьому прикладі сам зворотний виклик не реалізовано (це може бути лише один рядок коду), лише частину з документацією.

/// tip | Порада

Фактичний зворотний виклик - це просто HTTP-запит.

Реалізуючи зворотний виклик самостійно, ви можете скористатися, наприклад, [HTTPX](https://www.python-httpx.org) або [Requests](https://requests.readthedocs.io/).

///

## Напишіть код документації для зворотного виклику { #write-the-callback-documentation-code }

Цей код не виконуватиметься у вашому застосунку, він потрібен лише, щоб задокументувати, яким має бути зовнішній API.

Але ви вже знаєте, як легко створювати автоматичну документацію для API за допомогою FastAPI.

Тож ми скористаємося цими знаннями, щоб задокументувати, яким має бути зовнішній API... створивши операції шляху, які має реалізувати зовнішній API (ті, які викликатиме ваш API).

/// tip | Порада

Пишучи код для документування зворотного виклику, корисно уявити, що ви - той *зовнішній розробник*. І що ви зараз реалізуєте *зовнішній API*, а не *ваш API*.

Тимчасово прийнявши цю точку зору ( *зовнішнього розробника* ), вам буде очевидніше, куди помістити параметри, яку Pydantic-модель використати для тіла, для відповіді тощо для того *зовнішнього API*.

///

### Створіть callback `APIRouter` { #create-a-callback-apirouter }

Спочатку створіть новий `APIRouter`, який міститиме один або кілька зворотних викликів.

{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[1,23] *}

### Створіть операцію шляху зворотного виклику { #create-the-callback-path-operation }

Щоб створити операцію шляху зворотного виклику, використайте той самий `APIRouter`, який ви створили вище.

Вона має виглядати як звичайна операція шляху FastAPI:

- Ймовірно має містити оголошення тіла, яке вона приймає, напр. `body: InvoiceEvent`.
- І також може містити оголошення відповіді, яку вона повертає, напр. `response_model=InvoiceEventReceived`.

{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[14:16,19:20,26:30] *}

Є 2 основні відмінності від звичайної операції шляху:

- Їй не потрібен реальний код, адже ваш застосунок ніколи не викликатиме цей код. Вона використовується лише для документування зовнішнього API. Тому функція може просто містити `pass`.
- Шлях може містити [вираз OpenAPI 3](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression) (див. нижче), де можна використовувати змінні з параметрами та частини оригінального запиту, надісланого до вашого API.

### Вираз шляху зворотного виклику { #the-callback-path-expression }

Шлях зворотного виклику може містити [вираз OpenAPI 3](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression), який включає частини оригінального запиту, надісланого до вашого API.

У цьому випадку це строка:

```Python
"{$callback_url}/invoices/{$request.body.id}"
```

Отже, якщо користувач вашого API (зовнішній розробник) надішле запит до *вашого API* на:

```
https://yourapi.com/invoices/?callback_url=https://www.external.org/events
```

з JSON-тілом:

```JSON
{
    "id": "2expen51ve",
    "customer": "Mr. Richie Rich",
    "total": "9999"
}
```

тоді *ваш API* опрацює рахунок і згодом надішле запит зворотного виклику на `callback_url` ( *зовнішній API* ):

```
https://www.external.org/events/invoices/2expen51ve
```

з JSON-тілом на кшталт:

```JSON
{
    "description": "Payment celebration",
    "paid": true
}
```

і очікуватиме відповідь від того *зовнішнього API* з JSON-тілом на кшталт:

```JSON
{
    "ok": true
}
```

/// tip | Порада

Зверніть увагу, що використаний URL зворотного виклику містить URL, отриманий як параметр запиту в `callback_url` (`https://www.external.org/events`), а також `id` рахунку зсередини JSON-тіла (`2expen51ve`).

///

### Додайте маршрутизатор зворотного виклику { #add-the-callback-router }

На цьому етапі ви маєте потрібні операції шляху зворотного виклику (ті, які має реалізувати *зовнішній розробник* у *зовнішньому API*) у створеному вище маршрутизаторі зворотного виклику.

Тепер використайте параметр `callbacks` у декораторі операції шляху вашого API, щоб передати атрибут `.routes` (це насправді просто `list` маршрутів/операцій шляху) з цього маршрутизатора зворотного виклику:

{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[33] *}

/// tip | Порада

Зверніть увагу, що ви передаєте не сам маршрутизатор (`invoices_callback_router`) у `callback=`, а атрибут `.routes`, тобто `invoices_callback_router.routes`.

///

### Перевірте документацію { #check-the-docs }

Тепер ви можете запустити застосунок і перейти за адресою [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).

Ви побачите вашу документацію з розділом «Callbacks» для вашої операції шляху, який показує, як має виглядати зовнішній API:

<img src="/img/tutorial/openapi-callbacks/image01.png">
