REST, versioning, backward compatibility, pagination
Плохой APIломет клиентов навсегда. Хороший API расширяется без ломки старых клиентов.
# ❌ Плохо: глаголы в URL
POST /api/createUser
POST /api/updateUser/123
GET /api/deleteUser/123
# ✅ Хорошо: ресурсы + HTTP глаголы
POST /api/users # Создать
GET /api/users/123 # Получить
PUT /api/users/123 # Обновить целиком
PATCH /api/users/123 # Обновить частично
DELETE /api/users/123 # Удалить# ❌ Всегда 200
return jsonify({'error': 'Not found'}), 200
# ✅ Правильные статусы
return jsonify(user.to_dict()), 200 # OK
return jsonify(created_user.to_dict()), 201 # Created
return '', 204 # No Content
return jsonify({'error': 'Invalid'}), 400 # Bad Request
return jsonify({'error': 'Unauthorized'}), 401
return jsonify({'error': 'Forbidden'}), 403
return jsonify({'error': 'Not found'}), 404
return jsonify({'error': 'Conflict'}), 409
return jsonify({'error': 'Server error'}), 500# ✅ URL versioning (наиболее распространено)
GET /api/v1/users
GET /api/v2/users
# ✅ Header versioning
GET /api/users
Accept: application/vnd.myapi.v1+json
# ✅ Query parameter (менее желательно)
GET /api/users?version=1# ❌ Ломает старых клиентов
# Было:
{
"name": "John",
"age": 30
}
# Стало (убрали поле):
{
"full_name": "John", # Старые клиенты сломаются!
"years": 30
}
# ✅ Правильно: добавить новое, сохранить старое
{
"name": "John", # Оставили для совместимости
"full_name": "John", # Новое поле
"age": 30, # Оставили
"years": 30 # Новое поле (deprecated: используйте age)
}Правило: Никогда не удаляйте поля. Помечайте как deprecated, удаляйте в следующей мажорной версии.
# ✅ Offset-based (просто, но медленно на больших смещениях)
GET /api/users?page=2&limit=20
# Ответ:
{
"items": [...],
"total": 1000,
"page": 2,
"limit": 20,
"pages": 50,
"next": "/api/users?page=3&limit=20",
"prev": "/api/users?page=1&limit=20"
}
# ✅ Cursor-based (быстро для больших таблиц)
GET /api/users?cursor=eyJpZCI6MTAwfQ&limit=20
# Ответ:
{
"items": [...],
"next_cursor": "eyJpZCI6MTIwfQ",
"prev_cursor": "eyJpZCI6ODB9",
"has_more": true
}# ✅ Filtering
GET /api/users?status=active&role=admin
GET /api/users?created_at__gte=2024-01-01
# ✅ Sorting
GET /api/users?sort=-created_at,name # - для DESC
GET /api/users?order_by=age&direction=desc
# ✅ Field selection
GET /api/users?fields=id,name,email # Только нужные поля# ❌ Неясная ошибка
{
"error": "Something went wrong"
}
# ✅ Структурированная ошибка
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid input data",
"details": [
{"field": "email", "message": "Invalid email format"},
{"field": "age", "message": "Must be positive"}
]
}
}
# ✅ Для API ошибок
{
"error": {
"type": "insufficient_funds",
"code": "PAYMENT_001",
"message": "Insufficient funds on account",
"param": "account_balance",
"doc_url": "https://docs.api.com/errors/PAYMENT_001"
}
}# ✅ Заголовки rate limiting
HTTP/1.1 200 OK
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1640995200
# ✅ При превышении
HTTP/1.1 429 Too Many Requests
Retry-After: 60
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests",
"retry_after": 60
}
}Ключевая мысль: API — это контракт с клиентами. Меняйте его осторожно, сохраняйте backward compatibility, версионируйте ломающие изменения.
Вопросы ещё не добавлены
Вопросы для этой подтемы ещё не добавлены.