Proxy для API запросов

Google Maps JavaScript API в браузере изначально рассчитан на прямое взаимодействие клиента с серверами картографической платформы. Однако экосистема Google Maps включает не только клиентский рендеринг карт, но и множество HTTP-сервисов: Geocoding API, Places API, Distance Matrix API, Directions API. Именно эти REST-интерфейсы часто становятся причиной внедрения промежуточного слоя — прокси-сервера.

Прокси в контексте работы с API Google Maps представляет собой серверный компонент, через который проходят все запросы к внешним сервисам. Он принимает запросы от фронтенда, выполняет их от своего имени и возвращает результат обратно, при этом контролируя доступ, форматирование, кеширование и безопасность.

Ограничения клиентского доступа к API Google Maps

Прямые вызовы API из браузера сопровождаются рядом технических и архитектурных ограничений:

1. Утечка API-ключа API-ключ, встроенный в клиентский код, становится доступным любому пользователю через инструменты разработчика. Даже при ограничениях по HTTP-referer ключ может быть использован в обход сценария, например через серверные запросы.

2. Ограничения CORS Не все Google Maps API эндпоинты поддерживают кросс-доменные запросы из браузера без ограничений. Это особенно актуально для legacy REST API и кастомных интеграций.

3. Контроль квот и злоупотреблений Без промежуточного слоя невозможно ограничить частоту запросов от конкретного пользователя, что приводит к быстрому исчерпанию квоты API.

4. Отсутствие бизнес-логики Клиентская сторона не должна содержать сложную логику обработки геоданных, агрегации результатов или трансформации ответов.

Архитектурная роль прокси в геоинформационных приложениях

Прокси становится частью серверного слоя между интерфейсом пользователя и внешними API.

Типовая схема выглядит следующим образом:

  • Браузер → собственный backend (proxy)
  • Backend → Google Maps API
  • Backend → обработка/кеширование/валидация
  • Backend → возврат результата клиенту

Такой подход позволяет централизовать:

  • управление ключами доступа;
  • фильтрацию входящих запросов;
  • нормализацию ответов;
  • логирование и мониторинг.

Базовая реализация прокси на Node.js (Express)

Наиболее распространённый вариант — использование Node.js с фреймворком Express.

import express fr om 'express';
import fetch from 'node-fetch';

const app = express();
const PORT = 3000;

const GOOGLE_API_KEY = process.env.GOOGLE_API_KEY;

app.get('/api/geocode', async (req, res) => {
    const address = req.query.address;

    if (!address) {
        return res.status(400).json({ error: 'Address is required' });
    }

    const url = `https://maps.googleapis.com/maps/api/geocode/json?address=${encodeURIComponent(address)}&key=${GOOGLE_API_KEY}`;

    try {
        const response = await fetch(url);
        const data = await response.json();

        res.json(data);
    } catch (error) {
        res.status(500).json({ error: 'Proxy request failed' });
    }
});

app.listen(PORT, () => {
    console.log(`Proxy server running on port ${PORT}`);
});

В данной архитектуре API-ключ полностью скрыт от клиента, а взаимодействие с Google API происходит исключительно на серверной стороне.

Прокси как слой нормализации данных

Часто Google Maps API возвращает избыточные или нестабильные структуры данных. Прокси может выступать как слой трансформации.

Пример упрощения ответа Geocoding API:

app.get('/api/geocode', async (req, res) => {
    const address = req.query.address;

    const url = `https://maps.googleapis.com/maps/api/geocode/json?address=${encodeURIComponent(address)}&key=${GOOGLE_API_KEY}`;

    const response = await fetch(url);
    const data = await response.json();

    const result = data.results?.[0];

    const simplified = result ? {
        formattedAddress: result.formatted_address,
        location: result.geometry.location,
        placeId: result.place_id
    } : null;

    res.json(simplified);
});

Такой подход снижает нагрузку на клиент и стандартизирует формат данных.

Кэширование запросов для снижения нагрузки

Geocoding и Places API часто возвращают одинаковые результаты для повторяющихся запросов. Введение кеширования позволяет существенно сократить количество обращений к Google API.

Наиболее распространённое решение — Redis.

import Redis from 'ioredis';

const redis = new Redis();

app.get('/api/geocode', async (req, res) => {
    const address = req.query.address;
    const cacheKey = `geocode:${address}`;

    const cached = await redis.get(cacheKey);
    if (cached) {
        return res.json(JSON.parse(cached));
    }

    const url = `https://maps.googleapis.com/maps/api/geocode/json?address=${encodeURIComponent(address)}&key=${GOOGLE_API_KEY}`;
    const response = await fetch(url);
    const data = await response.json();

    await redis.set(cacheKey, JSON.stringify(data), 'EX', 86400);

    res.json(data);
});

Ключевые эффекты кеширования:

  • снижение стоимости API-запросов;
  • уменьшение задержек;
  • защита от повторяющихся запросов;
  • повышение стабильности системы.

Ограничение частоты запросов (rate limiting)

Прокси позволяет внедрить контроль нагрузки на уровне пользователей или IP-адресов.

Пример с использованием express-rate-lim it:

import rateLimit fr om 'express-rate-lim it';

const limiter = rateLimit({
    windowMs: 60 * 1000,
    max: 30,
    message: { error: 'Too many requests' }
});

app.use('/api/', limiter);

Это предотвращает:

  • автоматизированные злоупотребления;
  • резкие всплески нагрузки;
  • исчерпание квоты Google Maps API.

Serverless-прокси для Google Maps API

Вместо классического сервера прокси может быть реализован через serverless-инфраструктуру: Google Cloud Functions, AWS Lambda или Vercel Functions.

Пример Cloud Function:

const fetch = require('node-fetch');

exports.geocodeProxy = async (req, res) => {
    const address = req.query.address;
    const key = process.env.GOOGLE_API_KEY;

    const url = `https://maps.googleapis.com/maps/api/geocode/json?address=${encodeURIComponent(address)}&key=${key}`;

    const response = await fetch(url);
    const data = await response.json();

    res.status(200).send(data);
};

Особенности serverless-подхода:

  • автоматическое масштабирование;
  • оплата за фактические вызовы;
  • отсутствие постоянного сервера;
  • удобная интеграция с frontend-приложениями.

Прокси для Places API и фильтрация данных

Places API часто возвращает большое количество полей, которые не всегда необходимы клиенту. Прокси может фильтровать результат.

app.get('/api/places', async (req, res) => {
    const query = req.query.query;

    const url = `https://maps.googleapis.com/maps/api/place/textsearch/json?query=${encodeURIComponent(query)}&key=${GOOGLE_API_KEY}`;

    const response = await fetch(url);
    const data = await response.json();

    const simplified = data.results.map(place => ({
        name: place.name,
        rating: place.rating,
        location: place.geometry.location,
        placeId: place.place_id
    }));

    res.json(simplified);
});

Такой слой уменьшает объём передаваемых данных и повышает предсказуемость клиентского API.

Безопасность API-ключей и контроль доступа

Прокси позволяет реализовать многоуровневую защиту:

1. Сокрытие ключа API-ключ хранится только на сервере, в переменных окружения.

2. Ограничение доменов Даже при компрометации прокси можно ограничить доступ к нему по IP или токенам.

3. Авторизация пользователей Добавляется проверка JWT или session-based авторизации перед проксированием запроса.

function authMiddleware(req, res, next) {
    const token = req.headers.authorization;

    if (!token || token !== 'valid-token') {
        return res.status(403).json({ error: 'Forbidden' });
    }

    next();
}

app.use('/api/', authMiddleware);

Логирование и мониторинг запросов

Прокси становится точкой наблюдения за всеми обращениями к Google Maps API.

Возможные метрики:

  • количество запросов по эндпоинтам;
  • среднее время ответа;
  • процент ошибок API;
  • распределение запросов по пользователям.

Пример простого логирования:

app.use((req, res, next) => {
    console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
    next();
});

Для production-систем обычно используются специализированные системы наблюдения: Prometheus, Grafana, ELK Stack.

Обработка ошибок и устойчивость системы

Google Maps API может возвращать различные типы ошибок:

  • OVER_QUERY_LIMIT;
  • REQUEST_DENIED;
  • INVALID_REQUEST;
  • UNKNOWN_ERROR.

Прокси позволяет унифицировать обработку:

function handleGoogleError(status) {
    const map = {
        'OVER_QUERY_LIMIT': 429,
        'REQUEST_DENIED': 403,
        'INVALID_REQUEST': 400
    };

    return map[status] || 500;
}

Комбинирование прокси с Google Maps JavaScript API

Несмотря на то, что сам JavaScript API используется на клиенте для отображения карты, прокси часто дополняет его функциональность:

  • геокодирование адресов перед отображением маркеров;
  • поиск мест через backend;
  • расчёт маршрутов до визуализации;
  • агрегация нескольких источников геоданных.

Так формируется гибридная архитектура:

  • JavaScript API отвечает за визуализацию;
  • прокси отвечает за данные и бизнес-логику;
  • Google backend обеспечивает вычисления и геоданные.