Development

Проєктування API: найкращі практики та підходи

cyberwolf.studio
Development/10 min read/January 15, 2025
Danylo Kabanov
Danylo Kabanov
Chief Technology Officer

#Проєктування API: найкращі практики та підходи

API — це контракт між системами. Якість цього контракту визначає швидкість розробки, надійність інтеграцій і задоволення розробників, які з ним працюють. У CyberWolf.Studio ми розглядаємо API як продукт і проєктуємо його відповідно.

##Принципи проєктування

Незалежно від технології (REST, GraphQL, gRPC), ми дотримуємось фундаментальних принципів:

  • Консистентність — однакові конвенції у всіх ендпоінтах
  • Передбачуваність — розробник може вгадати структуру, не читаючи документацію
  • Зворотна сумісність — нові версії не ламають існуючих споживачів
  • Безпека за замовчуванням — автентифікація, авторизація, валідація

##REST API: конвенції та структура

Для більшості проєктів REST залишається оптимальним вибором:

typescript
1// Конвенції іменування ресурсів
2const apiRoutes = {
3 // Колекції — множина
4 "GET /api/v1/projects": "Список проєктів",
5 "POST /api/v1/projects": "Створити проєкт",
6
7 // Окремий ресурс — за ідентифікатором
8 "GET /api/v1/projects/:id": "Отримати проєкт",
9 "PATCH /api/v1/projects/:id": "Оновити проєкт",
10 "DELETE /api/v1/projects/:id": "Видалити проєкт",
11
12 // Вкладені ресурси — належність
13 "GET /api/v1/projects/:id/tasks": "Задачі проєкту",
14 "POST /api/v1/projects/:id/tasks": "Створити задачу",
15
16 // Дії — для операцій, що не вписуються в CRUD
17 "POST /api/v1/projects/:id/archive": "Архівувати проєкт",
18}

###Стандартизовані відповіді

Єдиний формат відповіді для всього API:

typescript
1// Успішна відповідь з пагінацією
2interface ApiResponse<T> {
3 data: T
4 meta?: {
5 page: number
6 perPage: number
7 total: number
8 totalPages: number
9 }
10}
11
12// Відповідь з помилкою
13interface ApiError {
14 error: {
15 code: string
16 message: string
17 details?: Record<string, string[]>
18 }
19}
20
21// Приклад: список проєктів
22// GET /api/v1/projects?page=1&perPage=20&status=active
23{
24 "data": [
25 {
26 "id": "proj_abc123",
27 "name": "Website Redesign",
28 "status": "active",
29 "createdAt": "2025-01-15T10:00:00Z"
30 }
31 ],
32 "meta": {
33 "page": 1,
34 "perPage": 20,
35 "total": 47,
36 "totalPages": 3
37 }
38}

##Валідація та обробка помилок

Кожен вхідний запит проходить через шар валідації:

typescript
1// Middleware для валідації запитів
2import { z } from "zod"
3
4const createTaskSchema = z.object({
5 title: z.string().min(1).max(200),
6 description: z.string().max(2000).optional(),
7 priority: z.enum(["low", "medium", "high", "critical"]),
8 assigneeId: z.string().uuid().optional(),
9 dueDate: z.string().datetime().optional(),
10 tags: z.array(z.string()).max(10).default([]),
11})
12
13export async function POST(request: Request) {
14 const body = await request.json()
15 const result = createTaskSchema.safeParse(body)
16
17 if (!result.success) {
18 return Response.json({
19 error: {
20 code: "VALIDATION_ERROR",
21 message: "Невалідні дані запиту",
22 details: result.error.flatten().fieldErrors,
23 },
24 }, { status: 400 })
25 }
26
27 const task = await taskService.create(result.data)
28 return Response.json({ data: task }, { status: 201 })
29}

##Автентифікація та авторизація

МетодВикористанняПереваги
API KeyСерверні інтеграціїПростота, швидкість
JWTКлієнтські застосункиStateless, масштабованість
OAuth 2.0Сторонні сервісиСтандарт індустрії, гранулярність
Session-basedВеб-застосункиБезпека, простота відкликання

###Rate limiting

Захист API від зловживань:

typescript
1// Конфігурація rate limiting
2const rateLimits = {
3 // Різні ліміти для різних рівнів доступу
4 free: {
5 requests: 100,
6 window: "1h",
7 burst: 10,
8 },
9 pro: {
10 requests: 1000,
11 window: "1h",
12 burst: 50,
13 },
14 enterprise: {
15 requests: 10000,
16 window: "1h",
17 burst: 200,
18 },
19}
20
21// Заголовки відповіді з інформацією про ліміти
22// X-RateLimit-Limit: 1000
23// X-RateLimit-Remaining: 847
24// X-RateLimit-Reset: 1706000000

##Версіонування

Ми використовуємо версіонування через URL-префікс — це найбільш прозорий підхід:

/api/v1/projects — стабільна версія /api/v2/projects — нова версія з breaking changes

###Правила еволюції API

  • Додавання полів — не є breaking change
  • Видалення полів — тільки в новій мажорній версії
  • Зміна типу поля — тільки в новій мажорній версії
  • Депрекація — мінімум 6 місяців з попередженням у відповіді

##Документація

API без документації — це API, яким ніхто не хоче користуватись. Наш підхід:

  • OpenAPI/Swagger — автогенерація зі схем
  • Інтерактивна пісочниця — можливість тестувати запити в браузері
  • Приклади коду — cURL, JavaScript, Python для кожного ендпоінту
  • Changelog — що змінилося між версіями
"

"Найкращий API — той, який розробник може інтегрувати за 15 хвилин, прочитавши лише Quick Start."

##Підсумки

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

***

Потрібна допомога з проєктуванням API? Напишіть нам — ми допоможемо створити API, яким приємно користуватись.

APIRESTGraphQLBackend