Каковы основные принципы проектирования RESTful API?

«Каковы основные принципы проектирования RESTful API?» — вопрос из категории Архитектура, который задают на 10% собеседований Python Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Проектирование RESTful API основано на наборе принципов, которые обеспечивают масштабируемость, простоту и предсказуемость. Ключевые из них:

  1. Ресурсная-ориентированность. API строится вокруг ресурсов (существительных), а не действий (глаголов).

    • Правильно: GET /users, POST /users
    • Неправильно: GET /getUsers, POST /createUser
  2. Использование стандартных HTTP-методов. Каждый метод имеет четкое семантическое значение:

    • GET: Получение ресурса или коллекции ресурсов.
    • POST: Создание нового ресурса.
    • PUT: Полное обновление существующего ресурса.
    • PATCH: Частичное обновление существующего ресурса.
    • DELETE: Удаление ресурса.
  3. Использование кодов состояния HTTP. Ответ сервера должен содержать корректный код состояния, информирующий клиента о результате операции.

    • 200 OK: Успешный запрос.
    • 201 Created: Ресурс успешно создан.
    • 204 No Content: Успешный запрос, но нет данных для ответа (например, после удаления).
    • 400 Bad Request: Ошибка в запросе клиента (например, невалидные данные).
    • 404 Not Found: Ресурс не найден.
    • 500 Internal Server Error: Ошибка на сервере.
  4. Версионирование. Для обратной совместимости API следует версионировать, чаще всего через URL.

    • Пример: /api/v1/users
  5. Фильтрация, сортировка и пагинация через параметры запроса (query parameters).

    • Пример: GET /users?status=active&sort=-createdAt&page=2&limit=100

Пример реализации на Flask:

from flask import Flask, jsonify, request

app = Flask(__name__)

# Получить всех пользователей (GET) или создать нового (POST)
@app.route('/api/v1/users', methods=['GET', 'POST'])
def handle_users():
    if request.method == 'POST':
        # Логика создания пользователя
        new_user = request.json
        return jsonify(new_user), 201 # Возвращаем созданный ресурс и статус 201
    else:
        # Логика получения списка пользователей
        users = [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]
        return jsonify(users), 200

# Получить, обновить или удалить конкретного пользователя
@app.route('/api/v1/users/<int:user_id>', methods=['GET', 'PUT', 'DELETE'])
def handle_user(user_id):
    if request.method == 'GET':
        # Логика поиска пользователя по user_id
        return jsonify({"id": user_id, "name": "Alice"}), 200
    elif request.method == 'PUT':
        # Логика полного обновления пользователя
        return jsonify(request.json), 200
    elif request.method == 'DELETE':
        # Логика удаления пользователя
        return '', 204 # Возвращаем пустой ответ и статус 204