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