О насБлогКонтакты
Backend разработка18 апреля 2012 г. 5 минОбновлено: 1 июля 2026 г.

Первый REST API для мобильного приложения: как мы учились проектировать API в 2012 году

AunimedaAunimeda
📋 Содержание

Весной 2012 года мы делали первый самостоятельный REST API для Android-приложения. До этого у нас были только веб-сайты - форм-сабмит, серверный рендеринг, сессии в куках. API для мобильного клиента был другим миром.

Мы прочли Roy Fielding's диссертацию (ну, её реферат). Прочли несколько статей на Хабре. И начали делать. Вот что вышло.


Ошибка №1: URL как имена процедур

Первая версия нашего API выглядела так:

GET  /api/getUser?id=123
POST /api/createOrder
GET  /api/getUserOrders?userId=123&status=active
POST /api/cancelOrder?orderId=456
GET  /api/getProductList?categoryId=2&page=1

Технически работало. Но это RPC через HTTP, не REST. Проблема проявилась через месяц: у нас было 40 endpoint'ов, все начинались с глагола, никакой структуры. Новый разработчик не мог предсказать URL - только смотреть в документацию.

Переписали по принципу «ресурсы, а не действия»:

GET    /api/v1/users/123              → получить пользователя
GET    /api/v1/users/123/orders       → заказы пользователя
GET    /api/v1/orders?status=active   → список заказов с фильтром
POST   /api/v1/orders                 → создать заказ
DELETE /api/v1/orders/456             → отменить заказ (не GET!)
GET    /api/v1/products?category=2    → список продуктов

Глаголы - HTTP-методы. Существительные - в URL. Новый разработчик мог угадать структуру.


Ошибка №2: API без версионирования

Первый месяц мы жили без версий в URL. Когда Android-клиент уже был у тестеров, нам потребовалось изменить структуру ответа /api/orders - добавить поле и переименовать существующее.

Мы изменили. Старый клиент сломался у тестеров. Нам повезло - приложение ещё не было в Play Store.

Версионирование через URL (/api/v1/) - самый простой вариант:

// routes.php - маршрутизация по версии
$version = $router->getVersion(); // Извлекает 'v1' из URL

switch ($version) {
    case 'v1':
        require 'controllers/v1/' . $controller . '.php';
        break;
    case 'v2':
        require 'controllers/v2/' . $controller . '.php';
        break;
    default:
        jsonResponse(400, ['error' => 'Unknown API version']);
}

Правило, которое мы приняли: v1 никогда не ломается. Новые фичи - в v2. Deprecated endpoint'ы в v1 живут ещё 6 месяцев, потом удаляются с предупреждением в заголовке.

// Deprecated endpoint: предупреждаем, но не ломаем
header('X-Deprecated: This endpoint will be removed on 2013-01-01. Use /v2/orders instead.');
// Возвращаем старую структуру ответа как обычно

Ошибка №3: токены в GET-параметрах

Первая аутентификация выглядела так:

GET /api/v1/orders?user_id=123&token=abc123def456

Проблема: токен в URL попадает в:

  • Логи сервера (access.log)
  • Историю браузера
  • Referrer-заголовок при переходе на другой сайт
  • Кэши прокси-серверов

Мы читали о HTTPS и думали, что это защищает. HTTPS шифрует передачу, но не мешает попаданию URL в логи на сервере.

Правильно: токен в заголовке Authorization:

GET /api/v1/orders
Authorization: Bearer abc123def456
// Извлечение токена из заголовка
function getAuthToken(): ?string {
    $header = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
    if (preg_match('/^Bearer\s+(.+)$/i', $header, $m)) {
        return $m[1];
    }
    return null;
}

function authenticate(): ?array {
    $token = getAuthToken();
    if (!$token) {
        jsonResponse(401, ['error' => 'Authorization required']);
        exit;
    }
    
    $user = User::findByToken($token);
    if (!$user || $user->token_expires_at < time()) {
        jsonResponse(401, ['error' => 'Invalid or expired token']);
        exit;
    }
    
    return $user;
}

Ошибка №4: HTTP 200 на всё подряд

Первое время мы возвращали HTTP 200 с полем success: false при ошибках. Это заставляло клиент парсить тело ответа, чтобы понять - успех это или ошибка:

// Плохо: 200 OK с признаком ошибки внутри
HTTP/1.1 200 OK
{"success": false, "error": "User not found"}

Правильно использовать семантику HTTP-статусов:

function jsonResponse(int $status, array $data): void {
    http_response_code($status);
    header('Content-Type: application/json; charset=utf-8');
    echo json_encode($data, JSON_UNESCAPED_UNICODE);
    exit;
}

// Использование
if (!$user) {
    jsonResponse(404, ['error' => 'User not found', 'code' => 'USER_NOT_FOUND']);
}
if (!$user->canAccessOrder($orderId)) {
    jsonResponse(403, ['error' => 'Access denied', 'code' => 'FORBIDDEN']);
}
if (!$validator->passes()) {
    jsonResponse(422, ['error' => 'Validation failed', 'fields' => $validator->errors()]);
}

// Успех
jsonResponse(200, ['data' => $order]);
// Создание ресурса
jsonResponse(201, ['data' => $newOrder, 'id' => $newOrder->id]);

Клиент на Android мог теперь проверить response.code() и обрабатывать ошибки без парсинга JSON:

if (!response.isSuccessful()) {
    // 4xx - ошибка клиента, показать пользователю
    // 5xx - ошибка сервера, показать "попробуйте позже"
    handleError(response.code());
    return;
}
// 2xx - обработать успешный ответ

Что получилось в итоге

Через три месяца и две итерации у нас был API, которым мы не стыдились:

  • URL-структура на основе ресурсов
  • Версионирование с /v1/ в пути
  • JWT-подобные токены в заголовке Authorization
  • Корректные HTTP-статусы
  • Единый формат ошибки {"error": "...", "code": "..."}
  • Документация в Markdown - по одной странице на endpoint

Первая версия API - это всегда обучение в бою. Важно не то, что вы сделали ошибки с версионированием и токенами. Важно что вы их заметили, пока приложение было у тестеров, а не у 50,000 пользователей с разными версиями клиента, которые все сломались одновременно.


Частые вопросы

Какие типичные ошибки делают при проектировании первого REST API?

Четыре классические: URL как имена процедур вместо ресурсов, отсутствие версионирования, токены в GET-параметрах (попадают в логи) и HTTP 200 на любой ответ, включая ошибки. Все они всплывают при росте нагрузки и числа версий клиента.

Как правильно версионировать API?

Закладывайте версию с самого начала — например, /v1/ в пути. Это позволяет менять API, не ломая старые версии мобильного клиента, которые остаются у пользователей надолго.

Где передавать токены авторизации?

В заголовке Authorization, а не в GET-параметрах: иначе токены попадают в логи серверов и историю браузера. Это базовое требование безопасности.

Что из этого опыта актуально в 2026 году?

Принципы REST неизменны: ресурсная структура URL, версионирование, корректные HTTP-статусы, единый формат ошибки и документация по endpoint'ам. Ошибки лучше ловить на тестерах, а не на 50 000 пользователей. Нужен надёжный API для приложения — закажите платный аудит.


Aunimeda — разработка backend и API на аутсорс для российских компаний с 2010 года. Студии в Бишкеке и Алматы. Обсудить проект →

Читайте также

Чистая архитектура в Node.js: практическое руководство без академизмаaunimeda
Backend разработка

Чистая архитектура в Node.js: практическое руководство без академизма

Чистая архитектура звучит хорошо в теории. На практике большинство реализаций добавляют сложность без пользы. Показываем паттерн, который реально работает в production Node.js TypeScript проектах - инверсия зависимостей, use cases, repository pattern с рабочим кодом.

Node.js vs Bun vs Deno 2026: какой JavaScript runtime выбратьaunimeda
Backend разработка

Node.js vs Bun vs Deno 2026: какой JavaScript runtime выбрать

Bun 1.x стабилен в production. Deno 2.0 поддерживает npm. Node.js 22 запускает TypeScript нативно. Реальные бенчмарки, сравнение экосистем и конкретные рекомендации - для новых и существующих проектов.

WebSocket-сервер на 100,000 соединений: архитектура и реальные подводные камниaunimeda
Backend разработка

WebSocket-сервер на 100,000 соединений: архитектура и реальные подводные камни

Как масштабировать WebSocket от 100 до 100k+ соединений: uWebSockets.js vs ws, Redis Pub/Sub между инстансами, heartbeat и reconnect логика, memory leak на неочищенных обработчиках.

Нужна IT-разработка для вашего бизнеса?

Разрабатываем сайты, мобильные приложения и AI-решения для бизнеса в России. Бесплатная консультация.

Разработка Telegram-ботов

Получить консультацию Все статьи