Генерація SDK¶
🌐 Переклад ШІ та людьми
Цей переклад виконано ШІ під керівництвом людей. 🤝
Можливі помилки через неправильне розуміння початкового змісту або неприродні формулювання тощо. 🤖
Ви можете покращити цей переклад, допомігши нам краще спрямовувати AI LLM.
Оскільки FastAPI базується на специфікації OpenAPI, його API можна описати у стандартному форматі, який розуміють багато інструментів.
Це спрощує створення актуальної документації, клієнтських бібліотек (SDKs) багатьма мовами, а також тестування чи автоматизованих робочих процесів, що залишаються синхронізованими з вашим кодом.
У цьому посібнику ви дізнаєтеся, як згенерувати TypeScript SDK для вашого бекенда на FastAPI.
Генератори SDK з відкритим кодом¶
Універсальним варіантом є OpenAPI Generator, який підтримує багато мов програмування та може генерувати SDK з вашої специфікації OpenAPI.
Для клієнтів TypeScript Hey API — спеціалізоване рішення, що надає оптимізований досвід для екосистеми TypeScript.
Більше генераторів SDK ви можете знайти на OpenAPI.Tools.
Порада
FastAPI автоматично генерує специфікації OpenAPI 3.1, тож будь-який інструмент, який ви використовуєте, має підтримувати цю версію.
Генератори SDK від спонсорів FastAPI¶
У цьому розділі представлено рішення від компаній, що спонсорують FastAPI: вони мають венчурну підтримку та корпоративну підтримку. Ці продукти надають додаткові можливості та інтеграції поверх високоякісно згенерованих SDK.
Завдяки ✨ спонсорству FastAPI ✨ ці компанії допомагають підтримувати фреймворк та його екосистему здоровими та сталими.
Їхня підтримка також демонструє сильну відданість спільноті FastAPI (вам), показуючи, що їм важливо не лише надавати відмінний сервіс, а й підтримувати міцний і процвітаючий фреймворк, FastAPI. 🙇
Наприклад, ви можете спробувати:
Деякі з цих рішень також можуть бути з відкритим кодом або мати безкоштовні тарифи, тож ви можете спробувати їх без фінансових зобов'язань. Інші комерційні генератори SDK також доступні й їх можна знайти онлайн. 🤓
Створити TypeScript SDK¶
Почнімо з простого застосунку FastAPI:
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
class ResponseMessage(BaseModel):
message: str
@app.post("/items/", response_model=ResponseMessage)
async def create_item(item: Item):
return {"message": "item received"}
@app.get("/items/", response_model=list[Item])
async def get_items():
return [
{"name": "Plumbus", "price": 3},
{"name": "Portal Gun", "price": 9001},
]
🤓 Other versions and variants
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
class ResponseMessage(BaseModel):
message: str
@app.post("/items/", response_model=ResponseMessage)
async def create_item(item: Item):
return {"message": "item received"}
@app.get("/items/", response_model=list[Item])
async def get_items():
return [
{"name": "Plumbus", "price": 3},
{"name": "Portal Gun", "price": 9001},
]
Зверніть увагу, що операції шляху визначають моделі, які вони використовують для корисного навантаження запиту та корисного навантаження відповіді, використовуючи моделі Item і ResponseMessage.
Документація API¶
Якщо ви перейдете до /docs, ви побачите схеми даних, які надсилаються в запитах і приймаються у відповідях:

Ви бачите ці схеми, оскільки їх було оголошено як моделі в застосунку.
Ця інформація доступна у схемі OpenAPI застосунку, а потім показується в документації API.
Та сама інформація з моделей, яку включено до OpenAPI, може бути використана для генерації клієнтського коду.
Hey API¶
Коли у нас є застосунок FastAPI з моделями, ми можемо використати Hey API для генерації клієнта TypeScript. Найшвидший спосіб зробити це — через npx.
npx @hey-api/openapi-ts -i http://localhost:8000/openapi.json -o src/client
Це згенерує TypeScript SDK у ./src/client.
Ви можете дізнатися, як встановити @hey-api/openapi-ts, і почитати про згенерований результат на їхньому сайті.
Використання SDK¶
Тепер ви можете імпортувати та використовувати клієнтський код. Це може виглядати так; зверніть увагу, що ви отримуєте «автодоповнення» для методів:

Ви також отримаєте автодоповнення для корисного навантаження, яке надсилаєте:

Порада
Зверніть увагу на автодоповнення для name і price, які були визначені в застосунку FastAPI, у моделі Item.
Ви бачитимете вбудовані помилки для даних, які надсилаєте:

Об'єкт відповіді також матиме автодоповнення:

Застосунок FastAPI з мітками¶
У багатьох випадках ваш застосунок FastAPI буде більшим, і ви, ймовірно, використовуватимете мітки, щоб розділяти різні групи операцій шляху.
Наприклад, у вас може бути секція для items і окрема секція для users, і їх можна розділити мітками:
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
class ResponseMessage(BaseModel):
message: str
class User(BaseModel):
username: str
email: str
@app.post("/items/", response_model=ResponseMessage, tags=["items"])
async def create_item(item: Item):
return {"message": "Item received"}
@app.get("/items/", response_model=list[Item], tags=["items"])
async def get_items():
return [
{"name": "Plumbus", "price": 3},
{"name": "Portal Gun", "price": 9001},
]
@app.post("/users/", response_model=ResponseMessage, tags=["users"])
async def create_user(user: User):
return {"message": "User received"}
🤓 Other versions and variants
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
class ResponseMessage(BaseModel):
message: str
class User(BaseModel):
username: str
email: str
@app.post("/items/", response_model=ResponseMessage, tags=["items"])
async def create_item(item: Item):
return {"message": "Item received"}
@app.get("/items/", response_model=list[Item], tags=["items"])
async def get_items():
return [
{"name": "Plumbus", "price": 3},
{"name": "Portal Gun", "price": 9001},
]
@app.post("/users/", response_model=ResponseMessage, tags=["users"])
async def create_user(user: User):
return {"message": "User received"}
Згенерувати TypeScript-клієнт із мітками¶
Якщо ви згенеруєте клієнт для застосунку FastAPI, що використовує мітки, зазвичай клієнтський код також буде розділено за цими мітками.
Таким чином, ви матимете правильно впорядковані та згруповані частини клієнтського коду:

У цьому випадку у вас є:
ItemsServiceUsersService
Назви методів клієнта¶
Зараз згенеровані назви методів на кшталт createItemItemsPost виглядають не дуже охайно:
ItemsService.createItemItemsPost({name: "Plumbus", price: 5})
...це тому, що генератор клієнта використовує внутрішній OpenAPI operation ID для кожної операції шляху.
OpenAPI вимагає, щоб кожен operation ID був унікальним для всіх операцій шляху, тому FastAPI використовує назву функції, шлях і HTTP-метод/операцію для генерації цього operation ID, адже так воно може гарантувати унікальність operation ID.
Але далі я покажу, як це покращити. 🤓
Користувацькі operation ID та кращі назви методів¶
Ви можете змінити спосіб генерації цих operation ID, щоб зробити їх простішими та мати простіші назви методів у клієнтах.
У цьому випадку вам потрібно буде іншим способом гарантувати, що кожен operation ID є унікальним.
Наприклад, ви можете переконатися, що кожна операція шляху має мітку, а потім генерувати operation ID на основі мітки та назви операції шляху (назви функції).
Користувацька функція генерування унікального ID¶
FastAPI використовує унікальний ID для кожної операції шляху, який застосовується для operation ID, а також для назв будь-яких потрібних користувацьких моделей для запитів чи відповідей.
Ви можете налаштувати цю функцію. Вона приймає APIRoute і повертає строку.
Наприклад, тут використовується перша мітка (у вас, ймовірно, буде лише одна мітка) і назва операції шляху (назва функції).
Потім ви можете передати цю користувацьку функцію до FastAPI як параметр generate_unique_id_function:
from fastapi import FastAPI
from fastapi.routing import APIRoute
from pydantic import BaseModel
def custom_generate_unique_id(route: APIRoute):
return f"{route.tags[0]}-{route.name}"
app = FastAPI(generate_unique_id_function=custom_generate_unique_id)
class Item(BaseModel):
name: str
price: float
class ResponseMessage(BaseModel):
message: str
class User(BaseModel):
username: str
email: str
@app.post("/items/", response_model=ResponseMessage, tags=["items"])
async def create_item(item: Item):
return {"message": "Item received"}
@app.get("/items/", response_model=list[Item], tags=["items"])
async def get_items():
return [
{"name": "Plumbus", "price": 3},
{"name": "Portal Gun", "price": 9001},
]
@app.post("/users/", response_model=ResponseMessage, tags=["users"])
async def create_user(user: User):
return {"message": "User received"}
🤓 Other versions and variants
from fastapi import FastAPI
from fastapi.routing import APIRoute
from pydantic import BaseModel
def custom_generate_unique_id(route: APIRoute):
return f"{route.tags[0]}-{route.name}"
app = FastAPI(generate_unique_id_function=custom_generate_unique_id)
class Item(BaseModel):
name: str
price: float
class ResponseMessage(BaseModel):
message: str
class User(BaseModel):
username: str
email: str
@app.post("/items/", response_model=ResponseMessage, tags=["items"])
async def create_item(item: Item):
return {"message": "Item received"}
@app.get("/items/", response_model=list[Item], tags=["items"])
async def get_items():
return [
{"name": "Plumbus", "price": 3},
{"name": "Portal Gun", "price": 9001},
]
@app.post("/users/", response_model=ResponseMessage, tags=["users"])
async def create_user(user: User):
return {"message": "User received"}
Згенерувати TypeScript-клієнт з користувацькими operation ID¶
Тепер, якщо ви згенеруєте клієнт знову, ви побачите покращені назви методів:

Як бачите, тепер у назвах методів є мітка, а потім назва функції; вони більше не містять інформації з URL-шляху та HTTP-операції.
Попередня обробка специфікації OpenAPI для генератора клієнта¶
У згенерованому коді все ще є певна дубльована інформація.
Ми вже знаємо, що цей метод стосується items, адже це слово є в ItemsService (взято з мітки), але все ще маємо назву мітки як префікс у назві методу. 😕
Ми, ймовірно, все одно захочемо зберегти це загалом для OpenAPI, адже так гарантується унікальність operation ID.
Але для згенерованого клієнта ми можемо змінити operation ID в OpenAPI безпосередньо перед генерацією клієнтів, просто щоб зробити назви методів приємнішими та чистішими.
Ми можемо завантажити JSON OpenAPI у файл openapi.json, а потім прибрати цей префікс із міткою за допомогою такого скрипту:
import json
from pathlib import Path
file_path = Path("./openapi.json")
openapi_content = json.loads(file_path.read_text())
for path_data in openapi_content["paths"].values():
for operation in path_data.values():
tag = operation["tags"][0]
operation_id = operation["operationId"]
to_remove = f"{tag}-"
new_operation_id = operation_id[len(to_remove) :]
operation["operationId"] = new_operation_id
file_path.write_text(json.dumps(openapi_content))
🤓 Other versions and variants
import json
from pathlib import Path
file_path = Path("./openapi.json")
openapi_content = json.loads(file_path.read_text())
for path_data in openapi_content["paths"].values():
for operation in path_data.values():
tag = operation["tags"][0]
operation_id = operation["operationId"]
to_remove = f"{tag}-"
new_operation_id = operation_id[len(to_remove) :]
operation["operationId"] = new_operation_id
file_path.write_text(json.dumps(openapi_content))
import * as fs from 'fs'
async function modifyOpenAPIFile(filePath) {
try {
const data = await fs.promises.readFile(filePath)
const openapiContent = JSON.parse(data)
const paths = openapiContent.paths
for (const pathKey of Object.keys(paths)) {
const pathData = paths[pathKey]
for (const method of Object.keys(pathData)) {
const operation = pathData[method]
if (operation.tags && operation.tags.length > 0) {
const tag = operation.tags[0]
const operationId = operation.operationId
const toRemove = `${tag}-`
if (operationId.startsWith(toRemove)) {
const newOperationId = operationId.substring(toRemove.length)
operation.operationId = newOperationId
}
}
}
}
await fs.promises.writeFile(
filePath,
JSON.stringify(openapiContent, null, 2),
)
console.log('File successfully modified')
} catch (err) {
console.error('Error:', err)
}
}
const filePath = './openapi.json'
modifyOpenAPIFile(filePath)
Після цього operation ID буде перейменовано з чогось на кшталт items-get_items просто на get_items, тож генератор клієнта зможе створити простіші назви методів.
Згенерувати TypeScript-клієнт із попередньо обробленим OpenAPI¶
Оскільки кінцевий результат тепер у файлі openapi.json, вам потрібно оновити шлях до вхідних даних:
npx @hey-api/openapi-ts -i ./openapi.json -o src/client
Після генерації нового клієнта ви отримаєте чисті назви методів, із усім автодоповненням, вбудованими помилками тощо:

Переваги¶
Використовуючи автоматично згенеровані клієнти, ви отримаєте автодоповнення для:
- Методів.
- Корисного навантаження запиту в тілі, параметрах запиту тощо.
- Корисного навантаження відповіді.
Також ви матимете вбудовані помилки для всього.
І щоразу, коли ви оновлюєте код бекенда та перегенеровуєте фронтенд, у ньому з'являтимуться нові операції шляху як методи, старі буде видалено, а будь-які інші зміни відобразяться у згенерованому коді. 🤓
Це також означає, що якщо щось змінилося, це буде відображено в клієнтському коді автоматично. А якщо ви зіберете клієнт, буде повідомлено про помилку, якщо є будь-яка невідповідність у використаних даних.
Таким чином, ви виявлятимете багато помилок дуже рано в циклі розробки, замість того, щоб чекати, поки помилки проявляться у ваших кінцевих користувачів у продакшені, і лише потім намагатися з'ясувати, у чому проблема. ✨