Зачем вашему проекту API Gateway на JavaScript?
Представьте, что ваш backend — это огромный торговый центр. Внутри него десятки магазинов (микросервисов): один продаёт книги, другой — одежду, третий — электронику. Каждый магазин имеет свой вход, свои правила и свой язык. А теперь представьте, что посетитель (клиентское приложение) должен обойти все эти входы, чтобы купить одну футболку. Хаос, правда?
В мире веб-разработки эту проблему решает API Gateway — единая точка входа для всех микросервисов. Он как администратор торгового центра: направляет запросы, проверяет билеты (токены), собирает данные из разных магазинов и отдаёт клиенту готовый ответ. И сегодня мы разберём, как написать такой шлюз на чистом JavaScript с использованием Node.js.
Эта статья будет полезна как новичкам, которые только знакомятся с архитектурой микросервисов, так и опытным разработчикам, которые хотят увидеть рабочие примеры кода. Поехали!
Что такое API Gateway и зачем он нужен?
API Gateway — это серверный компонент, который выступает промежуточным слоем между клиентом и внутренними сервисами. Он берёт на себя:
- Маршрутизацию — направляет запросы к нужному микросервису.
- Аутентификацию и авторизацию — проверяет, имеет ли пользователь доступ.
- Агрегацию данных — собирает ответы от нескольких сервисов в один.
- Лимитирование запросов — защищает backend от перегрузок.
- Трансформацию протоколов — например, конвертирует REST в GraphQL.
Для JavaScript сервисов (особенно на Node.js) API Gateway — это естественный выбор. Он легко пишется на Express, Koa или Fastify, а главное — позволяет сохранить единый стиль кода во всём проекте.
Проектируем структуру API Gateway на Node.js
Прежде чем писать код, давайте определимся с архитектурой. Допустим, у нас есть три микросервиса:
users-service— управление пользователями (порт 3001).orders-service— заказы (порт 3002).products-service— каталог товаров (порт 3003).
Клиент (фронтенд) обращается только к Gateway на порту 8080. Gateway сам решает, куда направить запрос.
Шаг 1. Базовая настройка Express
Создадим новый проект и установим зависимости:
mkdir api-gateway
cd api-gateway
npm init -y
npm install express http-proxy-middleware dotenv
Теперь напишем простой Gateway, который проксирует запросы к сервисам:
// gateway.js
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
require('dotenv').config();
const app = express();
const PORT = process.env.PORT || 8080;
// Маршруты для микросервисов
app.use('/users', createProxyMiddleware({
target: 'http://localhost:3001',
changeOrigin: true,
}));
app.use('/orders', createProxyMiddleware({
target: 'http://localhost:3002',
changeOrigin: true,
}));
app.use('/products', createProxyMiddleware({
target: 'http://localhost:3003',
changeOrigin: true,
}));
app.listen(PORT, () => {
console.log(`API Gateway запущен на порту ${PORT}`);
});
Вот и всё! Теперь любой запрос к /users автоматически перенаправляется на users-service. Но это только начало. Настоящий Gateway должен делать больше.
Добавляем аутентификацию и проверку токенов
Одна из главных задач API Gateway — централизованная аутентификация. Вместо того чтобы проверять JWT в каждом микросервисе, мы делаем это один раз в шлюзе.
Добавим middleware для проверки токена:
// middleware/auth.js
const jwt = require('jsonwebtoken');
const authMiddleware = (req, res, next) => {
const authHeader = req.headers.authorization;
if (!authHeader) {
return res.status(401).json({ error: 'Токен не предоставлен' });
}
const token = authHeader.split(' ')[1]; // Bearer <token>
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded; // добавляем данные пользователя в запрос
next();
} catch (err) {
return res.status(403).json({ error: 'Неверный токен' });
}
};
module.exports = authMiddleware;
Теперь подключим его к определённым маршрутам:
// gateway.js (фрагмент)
const authMiddleware = require('./middleware/auth');
// Защищённые маршруты (требуют токен)
app.use('/orders', authMiddleware, createProxyMiddleware({
target: 'http://localhost:3002',
changeOrigin: true,
}));
// Публичные маршруты (без аутентификации)
app.use('/products', createProxyMiddleware({
target: 'http://localhost:3003',
changeOrigin: true,
}));
Таким образом, мы создали единую точку входа с контролем доступа. Если токен просрочен или неверен — пользователь получит ошибку, не доходя до микросервиса.
Агрегация данных: собираем ответы из разных сервисов
Часто клиенту нужны данные из нескольких микросервисов одновременно. Например, чтобы отобразить страницу товара, нужно взять описание из products-service и отзывы из reviews-service. Без Gateway фронтенд делал бы два запроса. С Gateway — один.
Напишем эндпоинт-агрегатор:
// routes/aggregate.js
const axios = require('axios');
const aggregateProduct = async (req, res) => {
try {
const productId = req.params.id;
// Параллельные запросы к сервисам
const [productResponse, reviewsResponse] = await Promise.all([
axios.get(`http://localhost:3003/products/${productId}`),
axios.get(`http://localhost:3004/reviews/${productId}`),
]);
const productData = productResponse.data;
const reviewsData = reviewsResponse.data;
// Объединяем данные
const result = {
...productData,
reviews: reviewsData,
};
res.json(result);
} catch (error) {
res.status(500).json({ error: 'Ошибка агрегации данных' });
}
};
module.exports = { aggregateProduct };
Подключаем в Gateway:
const { aggregateProduct } = require('./routes/aggregate');
app.get('/aggregate/products/:id', aggregateProduct);
Теперь клиент получает полную информацию о товаре за один запрос. Это снижает нагрузку на сеть и ускоряет работу приложения.
Лимитирование запросов (Rate Limiting)
Без защиты ваш Gateway может стать жертвой DDoS-атаки или случайного всплеска трафика. Добавим лимитирование с помощью пакета express-rate-limit:
npm install express-rate-limit
// middleware/rateLimit.js
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 минут
max: 100, //