REST API: Rate limiting, файлы, deprecation

Rate limiting, загрузка файлов и deprecation эндпоинтов REST API.

REST API rate limiting deprecation

REST API rate limiting deprecation: 14. Rate limiting

14.1. HTTP-код -- 429 Too Many Requests

REST API rate limiting deprecation — при превышении лимита запросов сервер возвращает 429 с заголовком Retry-After, указывающим через сколько секунд клиент может повторить запрос:

HTTP/1.1 429 Too Many Requests
Retry-After: 30
Content-Type: application/problem+json

{
  "type": "about:blank",
  "status": 429,
  "title": "Too Many Requests",
  "detail": "Превышен лимит запросов. Повторите через 30 секунд.",
  "code": "RATE_LIMIT_EXCEEDED"
}

14.2. Заголовки лимитов в каждом ответе

В каждый успешный ответ включаются заголовки, информирующие клиента о текущем состоянии лимитов:

HTTP/1.1 200 OK
RateLimit-Limit: 100
RateLimit-Remaining: 57
RateLimit-Reset: 1719849600
  • RateLimit-Limit -- максимальное количество запросов в окне
  • RateLimit-Remaining -- сколько запросов осталось в текущем окне
  • RateLimit-Reset -- Unix timestamp когда окно сбрасывается

14.3. OpenAPI-описание

В контракте указывать 429 для эндпоинтов с rate limiting:

"429":
  description: 'Too Many Requests'
  headers:
    Retry-After:
      schema:
        type: integer
      description: 'Секунд до сброса лимита'
  content:
    application/problem+json:
      schema:
        $ref: "#/components/schemas/ProblemDetails"

15. Загрузка файлов

15.1. Эндпоинт загрузки

Файлы загружаются как вложенный ресурс через POST с multipart/form-data:

POST /api/v1/documents/{id}/attachments
POST /api/v1/users/me/avatar

15.2. Формат запроса

POST /api/v1/documents/{id}/attachments
Content-Type: multipart/form-data; boundary=----Boundary

------Boundary
Content-Disposition: form-data; name="file"; filename="report.pdf"
Content-Type: application/pdf

<binary data>
------Boundary
Content-Disposition: form-data; name="description"

Отчет за март
------Boundary--

15.3. Ограничения

Ограничения на размер и тип файлов указываются в OpenAPI и документации:

requestBody:
  content:
    multipart/form-data:
      schema:
        type: object
        required:
          - file
        properties:
          file:
            type: string
            format: binary
            description: 'Файл. Максимум 10 МБ. Допустимые типы: PDF, PNG, JPG'
          description:
            type: string
            maxLength: 500

15.4. Ответ

Код 201 Created. Тело -- метаданные загруженного файла:

{
  "attachmentId": "550e8400-e29b-41d4-a716-446655440000",
  "fileName": "report.pdf",
  "contentType": "application/pdf",
  "size": 1048576,
  "uploadedAt": "2025-03-15T10:30:00Z"
}

15.5. Скачивание файлов

GET /api/v1/documents/{id}/attachments/{id}

Ответ -- бинарное содержимое файла с соответствующим Content-Type:

HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Disposition: attachment; filename="report.pdf"
Content-Length: 1048576

<binary data>

16. Deprecation

16.1. Пометка в OpenAPI

Устаревший эндпоинт помечается deprecated: true с указанием альтернативы в description:

/api/v1/orders/{id}/status:
  get:
    deprecated: true
    summary: 'Получить статус заказа'
    description: 'DEPRECATED: используйте GET /api/v2/orders/{id}. Будет удалён после 2025-09-01.'

16.2. Заголовок Sunset

Устаревший эндпоинт возвращает заголовок Sunset (RFC 8594) с датой отключения:

HTTP/1.1 200 OK
Sunset: Sat, 01 Sep 2025 00:00:00 GMT
Deprecation: true
Link: </api/v2/orders/{id}>; rel="successor-version"
  • Sunset -- дата после которой эндпоинт будет удалён
  • Deprecation: true -- сигнал что эндпоинт устарел
  • Link с rel="successor-version" -- ссылка на замену

16.3. Процесс вывода из эксплуатации

  1. Пометить deprecated: true в OpenAPI, добавить заголовки Sunset и Deprecation
  2. Уведомить потребителей (changelog, рассылка, Slack)
  3. Мониторить трафик на устаревший эндпоинт
  4. После даты Sunset -- вернуть 410 Gone:
{
  "type": "about:blank",
  "status": 410,
  "title": "Gone",
  "detail": "Эндпоинт удалён. Используйте GET /api/v2/orders/{id}.",
  "code": "ENDPOINT_REMOVED"
}