GraphQL API с PHP: пошаговый туториал

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

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

Начать курс

Введение: Почему GraphQL и PHP — отличная пара

В мире веб-разработки REST API долгое время был стандартом де-факто. Однако с ростом сложности приложений и появлением мобильных клиентов, недостатки REST стали очевидны: переизбыток или недостаток данных, множество эндпоинтов, сложная версионизация. GraphQL, разработанный Facebook, решает эти проблемы, позволяя клиенту запрашивать только те данные, которые ему нужны, и получать их за один запрос.

PHP, несмотря на свою «старую школу», остается мощным языком для создания серверной логики. Благодаря современным библиотекам, таким как webonyx/graphql-php, реализовать GraphQL API на PHP стало просто и эффективно. В этом туториале мы шаг за шагом создадим полноценный GraphQL сервер, который сможет обрабатывать запросы, мутации и работать с базой данных.

Мы будем использовать PHP 8.0+, Composer для управления зависимостями и встроенный сервер PHP для локального тестирования. К концу статьи вы сможете развернуть собственное API и интегрировать его с любым фронтендом.

1. Установка и настройка окружения

Прежде чем писать код, необходимо подготовить проект. Убедитесь, что у вас установлены PHP 8.0+ и Composer. Создайте новую папку для проекта и перейдите в нее:

mkdir graphql-php-tutorial
cd graphql-php-tutorial

Теперь установите основную библиотеку webonyx/graphql-php с помощью Composer:

composer require webonyx/graphql-php

Для работы с базой данных мы будем использовать PDO, который встроен в PHP. Дополнительно установим библиотеку для удобного парсинга запросов:

composer require graphql-php/graphql-php

Создайте файл index.php — это будет точка входа в наше приложение. Настройте автозагрузку Composer и базовую структуру:

<?php
require_once __DIR__ . '/vendor/autoload.php';

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

// Здесь будет наша логика

Также создайте файл schema.graphql для хранения схемы в текстовом формате (опционально, но удобно для больших проектов).

2. Определение схемы и типов данных

Сердце любого GraphQL API — это схема. Она описывает, какие данные доступны, и как к ним обращаться. В нашем примере мы создадим простую модель пользователя (User) и поста (Post).

Начнем с определения типов. В файле index.php добавьте следующие классы:

// Тип User
$userType = new ObjectType([
    'name' => 'User',
    'fields' => [
        'id' => Type::int(),
        'name' => Type::string(),
        'email' => Type::string(),
        'posts' => [
            'type' => Type::listOf($postType),
            'resolve' => function ($user) use ($db) {
                // Функция-резолвер для загрузки постов пользователя
                $stmt = $db->prepare('SELECT * FROM posts WHERE user_id = :id');
                $stmt->execute([':id' => $user['id']]);
                return $stmt->fetchAll(PDO::FETCH_ASSOC);
            }
        ]
    ]
]);

// Тип Post
$postType = new ObjectType([
    'name' => 'Post',
    'fields' => [
        'id' => Type::int(),
        'title' => Type::string(),
        'content' => Type::string(),
        'author' => [
            'type' => $userType,
            'resolve' => function ($post) use ($db) {
                $stmt = $db->prepare('SELECT * FROM users WHERE id = :id');
                $stmt->execute([':id' => $post['user_id']]);
                return $stmt->fetch(PDO::FETCH_ASSOC);
            }
        ]
    ]
]);

Обратите внимание на резолверы — это функции, которые определяют, как получить данные для каждого поля. В примере выше мы используем PDO для запросов к базе данных.

Теперь создадим корневой тип Query, который будет точкой входа для чтения данных:

$queryType = new ObjectType([
    'name' => 'Query',
    'fields' => [
        'hello' => [
            'type' => Type::string(),
            'resolve' => function() {
                return 'Hello, GraphQL!';
            }
        ],
        'users' => [
            'type' => Type::listOf($userType),
            'resolve' => function() use ($db) {
                $stmt = $db->query('SELECT * FROM users');
                return $stmt->fetchAll(PDO::FETCH_ASSOC);
            }
        ],
        'user' => [
            'type' => $userType,
            'args' => [
                'id' => Type::nonNull(Type::int())
            ],
            'resolve' => function($root, $args) use ($db) {
                $stmt = $db->prepare('SELECT * FROM users WHERE id = :id');
                $stmt->execute([':id' => $args['id']]);
                return $stmt->fetch(PDO::FETCH_ASSOC);
            }
        ]
    ]
]);

3. Создание мутаций (Mutation) для изменения данных

GraphQL поддерживает не только чтение, но и запись данных через мутации. Добавим возможность создавать нового пользователя и поста.

$mutationType = new ObjectType([
    'name' => 'Mutation',
    'fields' => [
        'createUser' => [
            'type' => $userType,
            'args' => [
                'name' => Type::nonNull(Type::string()),
                'email' => Type::nonNull(Type::string())
            ],
            'resolve' => function($root, $args) use ($db) {
                $stmt = $db->prepare('INSERT INTO users (name, email) VALUES (:name, :email)');
                $stmt->execute([':name' => $args['name'], ':email' => $args['email']]);
                $id = $db->lastInsertId();
                $stmt = $db->prepare('SELECT * FROM users WHERE id = :id');
                $stmt->execute([':id' => $id]);
                return $stmt->fetch(PDO::FETCH_ASSOC);
            }
        ],
        'createPost' => [
            'type' => $postType,
            'args' => [
                'title' => Type::nonNull(Type::string()),
                'content' => Type::string(),
                'user_id' => Type::nonNull(Type::int())
            ],
            'resolve' => function($root, $args) use ($db) {
                $stmt = $db->prepare('INSERT INTO posts (title, content, user_id) VALUES (:title, :content, :user_id)');
                $stmt->execute([':title' => $args['title                

Блоги

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