GraphQL vs REST в PHP: что выбрать для вашего проекта в 2024?

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

GraphQL vs REST в PHP: война архитектур или инструменты для разных задач?

Когда вы в очередной раз открываете редактор кода, чтобы написать новый эндпоинт для вашего PHP-приложения, вас не покидает чувство дежавю. Снова создавать контроллер? Снова писать валидацию? А может, пора попробовать что-то новое? Спор между GraphQL и REST в мире PHP-разработки напоминает вечный спор между «Ларой» и «Симфони» — каждый защищает свой стек до хрипоты. Но давайте честно: оба подхода — это не религия, а инструменты. И сегодня мы разберем их на винтики, посмотрим на реальный код и решим, когда REST API на PHP — ваш выбор, а когда стоит присмотреться к GraphQL в PHP.

Короткий ликбез: что есть что?

REST (Representational State Transfer) — это архитектурный стиль, который построен на принципах ресурсов и HTTP-методов. Вы стучитесь на /api/users, получаете список юзеров. Хотите одного — добавьте /api/users/42. Всё логично, предсказуемо и банально.

GraphQL — это язык запросов, созданный Facebook. Вы не привязаны к URL. Вы отправляете один запрос на единый эндпоинт (например, /graphql) и говорите: «Дай мне имя пользователя 42, его последние 5 постов и количество лайков под каждым». Сервер возвращает ровно то, что вы попросили. Ни байтом больше.

Казалось бы, GraphQL — победитель по очкам. Но не спешите выбрасывать старый добрый REST. У каждого есть свои подводные камни, особенно в контексте разработки на PHP.

Сравнение архитектур: от теории к коду

REST: классика жанра

В PHP REST реализуется через маршрутизацию. Фреймворки вроде Laravel или Symfony делают это элегантно. Пример типичного REST-контроллера на Laravel:

<?php

namespace App\Http\Controllers\Api;

use App\Models\User;
use Illuminate\Http\Request;

class UserController extends Controller
{
    public function index()
    {
        return User::all();
    }

    public function show($id)
    {
        $user = User::with('posts.comments')->findOrFail($id);
        return response()->json($user);
    }

    public function store(Request $request)
    {
        $validated = $request->validate([
            'name' => 'required|string',
            'email' => 'required|email|unique:users',
        ]);

        $user = User::create($validated);
        return response()->json($user, 201);
    }
}

Плюсы очевидны: простота кэширования (HTTP-кеш, Varnish), понятная структура URL, широкая поддержка инструментов (Postman, Swagger). Минусы — проблема over-fetching и under-fetching. Если фронтенду нужны только имена пользователей, а вы в ответе тащите все поля модели — это over-fetching. Если нужно имя и список друзей, а вы делаете два запроса — это under-fetching.

GraphQL: гибкость с подвохом

Для реализации GraphQL на PHP есть отличные библиотеки: webonyx/graphql-php (чистый PHP) и lighthouse-php/lighthouse (для Laravel). Пример схемы и резолвера на graphql-php:

<?php

use GraphQL\GraphQL;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Schema;

$userType = new ObjectType([
    'name' => 'User',
    'fields' => [
        'id' => Type::int(),
        'name' => Type::string(),
        'email' => Type::string(),
        'posts' => [
            'type' => Type::listOf($postType),
            'resolve' => function ($user) {
                return getPostsByUserId($user['id']);
            }
        ],
    ],
]);

$queryType = new ObjectType([
    'name' => 'Query',
    'fields' => [
        'user' => [
            'type' => $userType,
            'args' => [
                'id' => Type::nonNull(Type::int()),
            ],
            'resolve' => function ($root, $args) {
                return findUserById($args['id']);
            }
        ],
    ],
]);

$schema = new Schema([
    'query' => $queryType,
]);

$rawInput = json_decode(file_get_contents('php://input'), true);
$result = GraphQL::executeQuery($schema, $rawInput['query']);
echo json_encode($result->toArray());

Фронтенд теперь может запросить только то, что нужно:

query {
  user(id: 42) {
    name
    posts {
      title
    }
  }
}

Красота? Да. Но за эту гибкость приходится платить сложностью кэширования и потенциальными проблемами с производительностью (N+1 запросы).

Производительность и оптимизация: где PHP пасует?

PHP — язык с блокирующим вводом-выводом. Это значит, что каждый запрос к базе данных в резолвере GraphQL — это последовательная операция. Если у вас 10 вложенных полей, каждый резолвер делает отдельный SQL-запрос. Получается классический N+1 problem. В REST вы бы просто сделали JOIN в контроллере и получили всё одним запросом.

Решение для GraphQL в PHP — использование DataLoader (паттерн батчинга). Библиотека overblog/dataloader-php позволяет группировать запросы. Пример:

<?php

use Overblog\DataLoader\DataLoader;

$userLoader = new DataLoader(function ($ids) {
    $users = User::whereIn('id', $ids)->get()->keyBy('id');
    return array_map(fn($id) => $users[$id] ?? null, $ids);
});

// В резолвере:
'resolve' => function ($root, $args) use ($userLoader) {
    return $userLoader->load($args['id']);
}

Но это добавляет сложность. В REST вы просто пишете один запрос с JOIN — и готово.

Когда выбирать REST, а когда GraphQL в PHP?

Вот чек-лист для принятия решения:

  • Выбирайте REST, если:
    • У вас простое CRUD-приложение (блог, новостной сайт).
    • Вы используете стандартное кэширование (HTTP ETag, Cache-Control).
    • У вас монолитное приложение с одной фронтенд-командой.
    • Вы хотите минимизировать сложность бэкенда.
  • Выбирайте GraphQL, если:
    • У вас сложные дашборды с динамическими запросами.
    • Несколько клиентов (мобильное приложение + веб) с разными потребностями.
    • Вы готовы пожертвовать кэшированием ради гибкости.
    • У вас сильная команда бэкенда, которая может написать Data

Блоги

Рекомендаций по книгам