Введение: Почему вашему PHP-проекту нужен API Gateway
Представьте, что вы строите современное веб-приложение. У вас есть сервис пользователей на Laravel, сервис заказов на Symfony, сервис платежей на чистом PHP, а еще внешние интеграции с CRM и телеграм-ботом. Клиенты (мобильное приложение, SPA на React и старый добрый jQuery-сайт) стучатся в разные эндпоинты, путаются в токенах, а вы каждую неделю чините CORS.
Знакомо? Если да, то вам нужен API Gateway — единая точка входа для всех ваших сервисов. Это не просто прокси, а настоящий диспетчер, который берет на себя маршрутизацию, аутентификацию, кэширование и агрегацию данных.
В этой статье мы разберем, как написать простой, но рабочий API Gateway на PHP, который можно развернуть на любом хостинге. Никакой магии, только чистый код и лучшие практики для микросервисной архитектуры на PHP.
Что такое API Gateway и зачем он нужен PHP-разработчику
API Gateway (шлюз API) — это сервер, который принимает все входящие запросы от клиентов и перенаправляет их к нужным микросервисам. Клиент ничего не знает о внутренней структуре: он просто шлет запрос на /api/users, а Gateway сам решает, куда его отправить.
Основные задачи шлюза:
- Маршрутизация — перенаправление запросов к разным сервисам на основе URL или заголовков.
- Аутентификация и авторизация — проверка JWT токенов, API-ключей, ограничение доступа.
- Агрегация — объединение данных из нескольких сервисов в один ответ (например, профиль пользователя + его последние заказы).
- Кэширование — снижение нагрузки на бэкенд за счет кэша часто запрашиваемых данных.
- Rate limiting — защита от DDoS и перегрузки сервисов.
- Трансформация ответов — приведение данных к единому формату для разных клиентов.
Плюсы очевидны: упрощение клиентской части, централизованное управление безопасностью, возможность менять бэкенд без уведомления клиентов. Минус — дополнительная точка отказа, но это решается репликацией Gateway.
Проектируем простой API Gateway на PHP
Мы напишем Gateway на чистом PHP без фреймворков, чтобы вы понимали суть. Но вы легко сможете адаптировать код под Laravel, Symfony или Slim.
Наш шлюз будет:
- Принимать запросы на
/api/users/*и перенаправлять в сервис Users (порт 8081) - Принимать запросы на
/api/orders/*и перенаправлять в сервис Orders (порт 8082) - Проверять JWT токен перед пропуском запроса
- Кэшировать GET-запросы на 60 секунд
- Агрегировать данные:
/api/profileбудет собирать информацию из Users и Orders
Шаг 1. Конфигурация маршрутов
Создадим файл config.php, где опишем, какие URL куда ведут:
<?php
// config.php
return [
'routes' => [
'/api/users' => [
'target' => 'http://127.0.0.1:8081',
'auth' => true, // требуется JWT
'cache' => false // не кэшируем (личные данные)
],
'/api/orders' => [
'target' => 'http://127.0.0.1:8082',
'auth' => true,
'cache' => 60 // кэш 60 секунд
],
'/api/public' => [
'target' => 'http://127.0.0.1:8083',
'auth' => false,
'cache' => 120
],
'/api/profile' => [ // агрегированный эндпоинт
'aggregate' => true,
'auth' => true,
'services' => [
'user' => 'http://127.0.0.1:8081/api/user',
'orders' => 'http://127.0.0.1:8082/api/orders/last'
]
]
]
];
Шаг 2. Ядро Gateway (index.php)
Теперь напишем главный файл, который принимает запросы, проверяет аутентификацию и выполняет проксирование:
<?php
// index.php — точка входа API Gateway
require_once 'config.php';
require_once 'AuthMiddleware.php';
require_once 'CacheMiddleware.php';
require_once 'ProxyService.php';
$config = require 'config.php';
$requestUri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$requestMethod = $_SERVER['REQUEST_METHOD'];
// Ищем подходящий маршрут
$route = null;
foreach ($config['routes'] as $prefix => $routeConfig) {
if (strpos($requestUri, $prefix) === 0) {
$route = $routeConfig;
$route['prefix'] = $prefix;
break;
}
}
if (!$route) {
http_response_code(404);
echo json_encode(['error' => 'Route not found']);
exit;
}
// Аутентификация
if ($route['auth'] ?? false) {
$auth = new AuthMiddleware();
$userId = $auth->validate();
if (!$userId) {
http_response_code(401);
echo json_encode(['error' => 'Unauthorized']);
exit;
}
}
// Агрегация
if ($route['aggregate'] ?? false) {
$proxy = new ProxyService();
$result = $proxy->aggregate($route['services'], $userId ?? null);
echo json_encode($result);
exit;
}
// Проксирование к сервису
$proxy = new ProxyService();
$targetUrl = $route['target'] . substr($requestUri, strlen($route['prefix']));
// Проверка кэша (только для GET)
$cache = new CacheMiddleware();
if ($requestMethod === 'GET' && ($route['cache'] ?? false)) {
$cached = $cache->get($targetUrl);
if ($cached) {
echo $cached;
exit;
}
}
// Выполняем запрос к сервису
$response = $proxy->forward($requestMethod, $targetUrl, getallheaders(), file_get_contents('php://input'));
// Кэшируем ответ
if ($requestMethod === 'GET' && ($route['cache'] ?? false)) {
$cache->set($targetUrl, $response, $route['cache']);
}
// Отдаем ответ клиенту
foreach ($response['headers'] as $header => $value) {
header("$header: $value");
}
http_response_code($response['status']);
echo $response['body'];
Шаг 3. Прокси-сервис (ProxyService.php)
Класс, который отправляет запросы к бэкенд-сервисам через cURL:
<?php
// ProxyService.php
class ProxyService
{
/**