Как работать с REST API в Google Apps Script: примеры и основы?

Что такое REST API: основные концепции и принципы

REST (Representational State Transfer) — это архитектурный стиль для создания веб-сервисов. REST API (Application Programming Interface) — это интерфейс, который позволяет различным программным системам взаимодействовать друг с другом через протокол HTTP. Ключевые концепции:

  • Ресурсы: Основная абстракция в REST. Это может быть любой объект или информация (например, пользователь, отчет, кампания). Ресурсы идентифицируются с помощью URI (Uniform Resource Identifier).
  • Методы HTTP: Стандартные глаголы HTTP используются для выполнения операций над ресурсами: GET (получение), POST (создание), PUT (обновление/замена), DELETE (удаление), PATCH (частичное обновление).
  • Представления: Клиенты работают не с самими ресурсами, а с их представлениями, обычно в формате JSON или XML.
  • Stateless (Отсутствие состояния): Каждый запрос от клиента к серверу должен содержать всю необходимую информацию для его выполнения. Сервер не хранит состояние клиента между запросами.
  • Клиент-серверная архитектура: Четкое разделение задач между клиентом (инициатор запроса) и сервером (обработчик запроса).

Преимущества использования REST API в Google Apps Script

Интеграция Google Workspace (Docs, Sheets, Forms, etc.) с внешними системами — мощный инструмент автоматизации. Использование REST API в Google Apps Script позволяет:

  • Автоматизировать обмен данными: Отправлять данные из Google Sheets в CRM, получать статистику из рекламных кабинетов, обновлять информацию на веб-сайтах.
  • Расширять функциональность Google Workspace: Создавать кастомные рабочие процессы, интегрируя сторонние сервисы (платежные системы, аналитические платформы, сервисы рассылок).
  • Централизовать управление: Собирать данные из разных источников в одном Google Sheet для анализа и отчетности.

Обзор сервиса UrlFetchApp в Google Apps Script

UrlFetchApp — это встроенный сервис Google Apps Script, который позволяет скриптам получать доступ к ресурсам в Интернете. Он является основным инструментом для взаимодействия с REST API. Сервис предоставляет методы для отправки HTTP-запросов (GET, POST, PUT, DELETE и др.) и обработки ответов.

Отправка GET запросов к REST API

Синтаксис UrlFetchApp.fetch() для GET запросов

Метод UrlFetchApp.fetch(url, params) используется для отправки HTTP-запросов. Для GET-запроса достаточно передать URL ресурса. Дополнительные параметры (заголовки, метод и т.д.) передаются во втором необязательном аргументе params.

Пример: Получение данных с открытого API (например, получение списка рекламных кампаний)

Предположим, у нас есть API для управления рекламными кампаниями.

/**
 * Получает список активных рекламных кампаний из внешнего API.
 *
 * @param {string} apiKey Ключ для аутентификации в API.
 * @returns {object | null} Объект с данными кампаний или null в случае ошибки.
 */
function getActiveCampaigns(apiKey) {
  const apiUrl = 'https://api.example-marketing.com/v1/campaigns?status=active';
  const options = {
    'method': 'get',
    'headers': {
      'Authorization': 'Bearer ' + apiKey
    },
    'contentType': 'application/json',
    // Параметр muteHttpExceptions позволяет обрабатывать ошибки HTTP вручную
    'muteHttpExceptions': true
  };

  try {
    const response = UrlFetchApp.fetch(apiUrl, options);
    const responseCode = response.getResponseCode();
    const responseBody = response.getContentText();

    if (responseCode === 200) {
      // Успешный запрос
      const campaigns = JSON.parse(responseBody);
      Logger.log('Получено кампаний: %s', campaigns.length);
      // Пример: Вывод названий кампаний
      campaigns.forEach(campaign => Logger.log('Название: %s, ID: %s', campaign.name, campaign.id));
      return campaigns; // Возвращаем массив объектов кампаний
    } else {
      // Обработка ошибок API (например, 401 Unauthorized, 404 Not Found)
      Logger.log('Ошибка API: Код ответа %s, Тело ответа: %s', responseCode, responseBody);
      return null;
    }
  } catch (error) {
    // Обработка сетевых ошибок или ошибок UrlFetchApp
    Logger.log('Внутренняя ошибка скрипта: %s', error);
    return null;
  }
}

Обработка ответа API: парсинг JSON

Большинство современных REST API возвращают данные в формате JSON. Метод response.getContentText() возвращает тело ответа в виде строки. Для преобразования этой строки в объект JavaScript используется встроенный метод JSON.parse().

Обработка ошибок при GET запросах (try…catch)

При работе с внешними API важно предусмотреть обработку ошибок. Это могут быть как сетевые проблемы, так и ошибки самого API (неверный запрос, отсутствие данных, проблемы с авторизацией).

  • Используйте блок try...catch для перехвата исключений, которые могут возникнуть во время выполнения UrlFetchApp.fetch().
  • Установите опцию 'muteHttpExceptions': true, чтобы UrlFetchApp.fetch() не выбрасывал исключение при кодах ответа HTTP >= 400. Это позволяет анализировать код ответа (response.getResponseCode()) и тело ответа (response.getContentText()) для диагностики проблемы.

Отправка POST запросов к REST API

Синтаксис UrlFetchApp.fetch() для POST запросов: параметры и опции

Для отправки POST-запроса необходимо указать метод 'post' в параметрах options и передать тело запроса (payload).

const options = {
  'method': 'post',
  'contentType': 'application/json',
  // 'payload' должен быть строкой
  'payload': JSON.stringify(dataObject),
  'headers': {
    'Authorization': 'Bearer YOUR_API_KEY'
  },
  'muteHttpExceptions': true
};
const response = UrlFetchApp.fetch(url, options);

Формирование payload (тела запроса) в формате JSON

Тело запроса (payload) содержит данные, отправляемые на сервер. Обычно это JavaScript-объект, который необходимо преобразовать в строку JSON с помощью JSON.stringify().

Пример: Отправка данных на сервер для создания нового ресурса (например, добавление лида в CRM)

/**
 * Создает нового лида в CRM через API.
 *
 * @param {string} apiKey API ключ для доступа к CRM.
 * @param {object} leadData Объект с данными лида (например, { firstName: 'Иван', lastName: 'Петров', email: 'ivan.p@example.com' }).
 * @returns {object | null} Объект с данными созданного лида или null в случае ошибки.
 */
function createCrmLead(apiKey, leadData) {
  const apiUrl = 'https://api.example-crm.com/v1/leads';
  const options = {
    'method': 'post',
    'contentType': 'application/json',
    'headers': {
      'Authorization': 'Bearer ' + apiKey
    },
    'payload': JSON.stringify(leadData),
    'muteHttpExceptions': true
  };

  try {
    const response = UrlFetchApp.fetch(apiUrl, options);
    const responseCode = response.getResponseCode();
    const responseBody = response.getContentText();

    if (responseCode === 201) { // 201 Created - стандартный код успешного создания
      const newLead = JSON.parse(responseBody);
      Logger.log('Лид успешно создан. ID: %s', newLead.id);
      return newLead;
    } else {
      Logger.log('Ошибка создания лида: Код ответа %s, Тело ответа: %s', responseCode, responseBody);
      return null;
    }
  } catch (error) {
    Logger.log('Внутренняя ошибка скрипта: %s', error);
    return null;
  }
}

// Пример вызова
// const newLeadData = { firstName: 'Сергей', lastName: 'Иванов', email: 'sergey.i@example.com', source: 'Website Form' };
// createCrmLead('YOUR_CRM_API_KEY', newLeadData);
Реклама

Установка заголовков запроса (headers): Content-Type, Authorization

Заголовки (headers) передают метаинформацию о запросе.

  • Content-Type: application/json: Указывает серверу, что тело запроса (payload) отформатировано как JSON.
  • Authorization: Используется для передачи учетных данных (API ключи, токены OAuth 2.0). Формат зависит от требований API (например, Bearer <token>, Basic <base64-credentials>).

Работа с другими типами запросов: PUT и DELETE

Отправка PUT запросов: обновление существующего ресурса

PUT-запросы используются для полного обновления существующего ресурса. Синтаксис аналогичен POST, но используется метод 'put'. Обычно требуется указать идентификатор обновляемого ресурса в URL.

/**
 * Обновляет статус рекламной кампании.
 *
 * @param {string} apiKey API ключ.
 * @param {string} campaignId Идентификатор кампании.
 * @param {string} newStatus Новый статус ('active', 'paused', 'removed').
 * @returns {boolean} true в случае успеха, иначе false.
 */
function updateCampaignStatus(apiKey, campaignId, newStatus) {
  const apiUrl = `https://api.example-marketing.com/v1/campaigns/${campaignId}`;
  const payload = JSON.stringify({ status: newStatus });
  const options = {
    'method': 'put',
    'contentType': 'application/json',
    'headers': { 'Authorization': 'Bearer ' + apiKey },
    'payload': payload,
    'muteHttpExceptions': true
  };

  try {
    const response = UrlFetchApp.fetch(apiUrl, options);
    if (response.getResponseCode() === 200 || response.getResponseCode() === 204) { // 200 OK или 204 No Content
      Logger.log('Статус кампании %s обновлен на %s', campaignId, newStatus);
      return true;
    } else {
      Logger.log('Ошибка обновления кампании %s: Код %s, Тело %s', campaignId, response.getResponseCode(), response.getContentText());
      return false;
    }
  } catch (error) {
    Logger.log('Внутренняя ошибка скрипта при обновлении кампании: %s', error);
    return false;
  }
}

Отправка DELETE запросов: удаление ресурса

DELETE-запросы используются для удаления ресурса. Синтаксис еще проще: указывается метод 'delete' и URL с идентификатором ресурса. Тело запроса (payload) обычно не требуется.

/**
 * Удаляет тестового лида из CRM.
 *
 * @param {string} apiKey API ключ.
 * @param {string} leadId Идентификатор лида для удаления.
 * @returns {boolean} true в случае успеха, иначе false.
 */
function deleteCrmLead(apiKey, leadId) {
  const apiUrl = `https://api.example-crm.com/v1/leads/${leadId}`;
  const options = {
    'method': 'delete',
    'headers': { 'Authorization': 'Bearer ' + apiKey },
    'muteHttpExceptions': true
  };

  try {
    const response = UrlFetchApp.fetch(apiUrl, options);
    if (response.getResponseCode() === 200 || response.getResponseCode() === 204) { // 200 OK или 204 No Content
      Logger.log('Лид %s успешно удален.', leadId);
      return true;
    } else {
      Logger.log('Ошибка удаления лида %s: Код %s, Тело %s', leadId, response.getResponseCode(), response.getContentText());
      return false;
    }
  } catch (error) {
    Logger.log('Внутренняя ошибка скрипта при удалении лида: %s', error);
    return false;
  }
}

Особенности использования PUT и DELETE запросов в Google Apps Script

Принципиальных отличий в использовании UrlFetchApp для PUT и DELETE нет. Важно корректно указывать метод ('put' или 'delete') в опциях и следовать спецификации конкретного API (формат URL, необходимость payload для PUT, коды успешного ответа).

Продвинутые техники и лучшие практики

Аутентификация и авторизация: OAuth 2.0 и API ключи

Многие API требуют аутентификации.

  • API Ключи: Простой способ, часто передаются в заголовке Authorization или как параметр URL. Легко реализуется с UrlFetchApp.
  • OAuth 2.0: Более сложный, но безопасный стандарт для делегирования доступа. Google Apps Script предоставляет встроенный сервис OAuth2 (в виде библиотеки) для упрощения потоков OAuth 2.0, особенно для работы с Google API, но может быть адаптирован и для сторонних сервисов.

Обработка пагинации при получении больших объемов данных

API часто возвращают большие списки данных частями (страницами). В ответе обычно присутствует информация для запроса следующей страницы (например, nextPageToken, next_page_url, параметры offset и limit). Необходимо в цикле запрашивать данные, пока не будут получены все страницы, или пока не будет достигнут нужный лимит.

/**
 * Получает все элементы из пагинированного API.
 *
 * @param {string} initialUrl Начальный URL для запроса первой страницы.
 * @param {object} baseOptions Базовые опции для fetch (метод, заголовки).
 * @returns {Array<object>} Массив всех полученных элементов.
 */
function fetchAllPaginatedData(initialUrl, baseOptions) {
  let allItems = [];
  let nextUrl = initialUrl;

  while (nextUrl) {
    try {
      const response = UrlFetchApp.fetch(nextUrl, baseOptions);
      const responseCode = response.getResponseCode();
      const responseBody = response.getContentText();

      if (responseCode === 200) {
        const data = JSON.parse(responseBody);
        if (data && data.items) { // Предполагаем, что данные в поле 'items'
          allItems = allItems.concat(data.items);
        }
        // Ищем URL следующей страницы (название поля зависит от API)
        nextUrl = data.nextPageLink || data.pagination?.next_url || null;
        if (nextUrl) {
           Logger.log('Запрос следующей страницы: %s', nextUrl);
           Utilities.sleep(500); // Небольшая задержка между запросами
        }
      } else {
        Logger.log('Ошибка API при пагинации: URL %s, Код %s, Тело %s', nextUrl, responseCode, responseBody);
        nextUrl = null; // Прерываем цикл при ошибке
      }
    } catch (error) {
      Logger.log('Внутренняя ошибка скрипта при пагинации: %s', error);
      nextUrl = null; // Прерываем цикл при ошибке
    }
  }
  Logger.log('Всего получено элементов: %s', allItems.length);
  return allItems;
}

Кэширование ответов API для оптимизации производительности

Если данные из API изменяются нечасто, их можно кэшировать с помощью сервиса CacheService. Это снижает количество запросов к API, ускоряет выполнение скрипта и помогает не превышать квоты API.

/**
 * Получает данные из API с использованием кэша.
 *
 * @param {string} cacheKey Ключ для кэширования.
 * @param {string} apiUrl URL для запроса данных.
 * @param {object} options Опции для UrlFetchApp.fetch.
 * @param {number} expirationInSeconds Время жизни кэша в секундах (макс. 21600).
 * @returns {object | null} Распарсенный JSON-ответ или null.
 */
function fetchDataWithCache(cacheKey, apiUrl, options, expirationInSeconds = 3600) {
  const cache = CacheService.getScriptCache();
  const cached = cache.get(cacheKey);

  if (cached != null) {
    Logger.log('Данные получены из кэша для ключа: %s', cacheKey);
    return JSON.parse(cached);
  }

  Logger.log('Запрос данных из API: %s', apiUrl);
  try {
    const response = UrlFetchApp.fetch(apiUrl, options);
    const responseCode = response.getResponseCode();
    const responseBody = response.getContentText();

    if (responseCode === 200) {
      cache.put(cacheKey, responseBody, expirationInSeconds);
      return JSON.parse(responseBody);
    } else {
      Logger.log('Ошибка API: Код %s, Тело %s', responseCode, responseBody);
      return null;
    }
  } catch (error) {
    Logger.log('Внутренняя ошибка скрипта: %s', error);
    return null;
  }
}

Использование библиотек и сторонних сервисов для упрощения работы с REST API

Хотя UrlFetchApp достаточно мощный, для сложных API (особенно с OAuth 2.0) можно использовать готовые библиотеки Apps Script (если они доступны для нужного сервиса) или даже промежуточные сервисы-коннекторы (например, Zapier, Make), если прямая интеграция оказывается слишком трудоемкой. Однако для большинства задач достаточно возможностей UrlFetchApp.


Добавить комментарий