Что такое Google Apps Script и его возможности
Google Apps Script (GAS) — это облачная платформа для разработки на JavaScript, которая позволяет автоматизировать задачи, интегрировать и расширять функциональность продуктов Google Workspace (Sheets, Docs, Forms, Drive, Gmail и т.д.). GAS предоставляет доступ к обширным API сервисов Google и внешних сервисов через UrlFetchApp.
Концепция API и зачем развертывать скрипт как API
API (Application Programming Interface) — это контракт, определяющий способы взаимодействия различных программных компонентов. Развертывание скрипта GAS как API позволяет вызывать его функции из внешних приложений (веб-сайтов, мобильных приложений, других скриптов) по протоколу HTTP.
Это открывает возможности для создания кастомных бэкендов, микросервисов для обработки данных из Google Workspace, интеграции с CRM, системами аналитики и другими внешними платформами без необходимости поддерживать собственную серверную инфраструктуру.
Преимущества использования Apps Script в качестве API
- Бессерверность: Нет необходимости управлять серверами, Google берет на себя инфраструктурные задачи.
- Интеграция с Google Workspace: Легкий доступ к данным и сервисам Google Sheets, Docs, Drive и т.д.
- Простота развертывания: Встроенные механизмы для публикации скрипта как веб-приложения.
- Бесплатный уровень: Щедрые квоты для большинства сценариев использования.
- Знакомая среда: Использование JavaScript и стандартных веб-технологий.
Подготовка скрипта Google Apps Script к развертыванию
Создание нового скрипта или использование существующего
Вы можете создать новый проект Apps Script через script.google.com или из контейнера (например, Google Sheet: Инструменты -> Редактор скриптов). Для API подходят как автономные (standalone), так и привязанные к контейнеру (container-bound) скрипты.
Написание функций, которые будут доступны через API
Для обработки HTTP-запросов используются специальные функции-триггеры:
doGet(e): Обрабатывает GET-запросы.doPost(e): Обрабатывает POST-запросы.
Параметр e (event object) содержит информацию о запросе, включая параметры и тело запроса.
Обработка параметров запроса (query parameters) и тела запроса (request body)
Параметры GET-запроса доступны через e.parameter (для одного значения) и e.parameters (для нескольких значений одного параметра). Тело POST-запроса доступно через e.postData.contents.
/**
* Обрабатывает GET-запросы, извлекая данные о кампаниях из Google Sheet.
* @param {GoogleAppsScript.Events.DoGet} e - Объект события, содержащий параметры запроса.
* @returns {GoogleAppsScript.Content.TextOutput} - JSON с данными или сообщением об ошибке.
*/
function doGet(e: GoogleAppsScript.Events.DoGet): GoogleAppsScript.Content.TextOutput {
let campaignId: string | null = null;
let statusFilter: string | null = null;
// Получение параметров запроса
if (e.parameter.campaignId) {
campaignId = e.parameter.campaignId;
}
if (e.parameter.status) {
statusFilter = e.parameter.status;
}
// Пример: Логика получения данных (заглушка)
const data = fetchCampaignData(campaignId, statusFilter);
return ContentService.createTextOutput(JSON.stringify(data))
.setMimeType(ContentService.MimeType.JSON);
}
/**
* Обрабатывает POST-запросы для добавления нового лида.
* @param {GoogleAppsScript.Events.DoPost} e - Объект события, содержащий тело запроса.
* @returns {GoogleAppsScript.Content.TextOutput} - JSON с результатом операции.
*/
function doPost(e: GoogleAppsScript.Events.DoPost): GoogleAppsScript.Content.TextOutput {
let response: { success: boolean; message: string; leadId?: string };
try {
if (!e.postData || !e.postData.contents) {
throw new Error('Request body is missing');
}
const leadData: { name: string; email: string; source: string } = JSON.parse(e.postData.contents);
// Валидация входных данных
if (!leadData.name || !leadData.email || !leadData.source) {
throw new Error('Missing required fields: name, email, source');
}
if (!validateEmail(leadData.email)) {
throw new Error('Invalid email format');
}
// Пример: Логика добавления лида (заглушка)
const newLeadId = addLeadToCRM(leadData);
response = { success: true, message: 'Lead added successfully', leadId: newLeadId };
return ContentService.createTextOutput(JSON.stringify(response))
.setMimeType(ContentService.MimeType.JSON);
} catch (error) {
Logger.log(`Error in doPost: ${error.message}`);
response = { success: false, message: error.message || 'An error occurred' };
// Возвращаем корректный HTTP статус ошибки, например 400 Bad Request
return ContentService.createTextOutput(JSON.stringify(response))
.setMimeType(ContentService.MimeType.JSON);
// Примечание: Apps Script не позволяет напрямую установить код ответа HTTP (всегда 200 OK или редирект 302).
// Статус ошибки передается в теле ответа.
}
}
// --- Вспомогательные функции (примеры) ---
/**
* Заглушка для функции получения данных о кампаниях.
* @param {string | null} id - ID кампании.
* @param {string | null} status - Фильтр по статусу.
* @returns {object[]} - Массив объектов кампаний.
*/
function fetchCampaignData(id: string | null, status: string | null): object[] {
// Здесь должна быть реальная логика чтения из Google Sheet или другого источника
Logger.log(`Fetching data for campaignId: ${id}, status: ${status}`);
// Пример возвращаемых данных
const allData = [
{ id: 'cmp1', name: 'Весенняя акция', status: 'active', budget: 1000 },
{ id: 'cmp2', name: 'Летняя распродажа', status: 'paused', budget: 500 },
{ id: 'cmp3', name: 'Новогоднее предложение', status: 'active', budget: 1500 }
];
return allData.filter(campaign =>
(!id || campaign.id === id) &&
(!status || campaign.status === status)
);
}
/**
* Заглушка для функции добавления лида в CRM.
* @param {object} data - Данные лида.
* @returns {string} - ID созданного лида.
*/
function addLeadToCRM(data: { name: string; email: string; source: string }): string {
// Реальная логика взаимодействия с CRM или Google Sheet
Logger.log(`Adding lead: ${JSON.stringify(data)}`);
const newId = 'lead_' + Math.random().toString(36).substring(2, 9);
// Например, добавление строки в Google Sheet
// SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Leads').appendRow([newId, data.name, data.email, data.source, new Date()]);
return newId;
}
/**
* Простая валидация email.
* @param {string} email - Email для проверки.
* @returns {boolean} - True, если email валиден.
*/
function validateEmail(email: string): boolean {
const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(String(email).toLowerCase());
}
Валидация входных данных и обработка ошибок
Критически важно валидировать все входные данные (e.parameter, e.postData.contents), чтобы предотвратить ошибки и обеспечить безопасность. Используйте блоки try...catch для перехвата исключений и возвращайте осмысленные сообщения об ошибках в формате JSON. Функция ContentService используется для формирования HTTP-ответа.
Развертывание скрипта как Web App (веб-приложения)
Настройка параметров развертывания (доступ, права доступа)
- В редакторе скриптов перейдите в раздел «Развертывание» -> «Новое развертывание».
- Выберите тип развертывания: «Веб-приложение».
- Заполните описание (опционально).
Выбор типа доступа: только я, любой пользователь, анонимный доступ
- Веб-приложение:
- Выполнять как:
- Я: Скрипт будет выполняться от вашего имени, используя ваши разрешения. Подходит для доступа к вашим личным данным.
- Пользователь, обращающийся к приложению: Скрипт будет выполняться от имени пользователя, который вызывает API. Пользователю потребуется авторизовать скрипт при первом обращении. Подходит для работы с данными пользователя.
- Кто имеет доступ:
- Только я: Доступ только для вашего Google-аккаунта.
- Все пользователи в домене : Доступ для пользователей вашего Google Workspace.
- Все: Анонимный доступ для любого пользователя в интернете.
- Выполнять как:
Важно: Для создания публичного API, доступного внешним системам без аутентификации Google, выберите «Выполнять как: Я» и «Кто имеет доступ: Все». Это самый распространенный вариант для API.
Публикация скрипта как веб-приложения
Нажмите «Развернуть». При первом развертывании или при изменении областей действия (scopes) потребуется предоставить авторизацию скрипту.
Получение URL-адреса развернутого API
После успешного развертывания будет предоставлен URL вида https://script.google.com/macros/s/ВАШ_ИДЕНТИФИКАТОР_РАЗВЕРТЫВАНИЯ/exec. Этот URL является конечной точкой (endpoint) вашего API.
Примечание: При каждом изменении кода скрипта необходимо создавать новое развертывание («Развертывание» -> «Управление развертываниями» -> Выбрать активное развертывание -> Карандаш (Редактировать) -> Выбрать «Новая версия» -> «Развернуть»), чтобы изменения вступили в силу для URL /exec. Тестовый URL /dev всегда отражает последнюю сохраненную версию кода.
Тестирование и использование API
Тестирование API с помощью Postman, curl или других инструментов
Используйте URL /exec, полученный при развертывании, для отправки запросов.
- GET-запрос (curl):
bash
curl -L "https://script.google.com/macros/s/YOUR_DEPLOYMENT_ID/exec?campaignId=cmp1&status=active"
- POST-запрос (curl):
bash
curl -L -X POST -H "Content-Type: application/json" \
-d '{"name":"John Doe","email":"john.doe@example.com","source":"Website Form"}' \
"https://script.google.com/macros/s/YOUR_DEPLOYMENT_ID/exec"
- Postman: Создайте новый запрос, выберите метод (GET/POST), введите URL, добавьте параметры на вкладке «Params» (для GET) или тело запроса на вкладке «Body» (выбрав «raw» и «JSON» для POST), и нажмите «Send».
Отправка запросов с параметрами и телом запроса
Параметры для doGet передаются как query string (?key1=value1&key2=value2). Тело запроса для doPost передается в теле HTTP-запроса, обычно в формате JSON с соответствующим заголовком Content-Type: application/json.
Обработка ответов API и кодов состояния HTTP
Apps Script Web App всегда возвращает HTTP статус 200 OK (или 302 Found при редиректах, которые обрабатываются автоматически клиентами вроде curl -L или Postman). Поэтому статус операции (успех/ошибка) и данные должны передаваться в теле ответа, как правило, в формате JSON. Клиентское приложение должно парсить JSON и анализировать его содержимое для определения результата.
Примеры использования API в других приложениях и сервисах
- Веб-форма: JavaScript на фронтенде может отправлять данные формы на ваш API с помощью
fetchилиXMLHttpRequest. - Мобильное приложение: Нативное или гибридное приложение может вызывать API для сохранения или получения данных, связанных с Google Workspace.
- Другой Apps Script: Использование
UrlFetchApp.fetch()для взаимодействия между скриптами. - Zapier/Integromat/Make: Интеграционные платформы могут вызывать ваш API как webhook.
- Системы аналитики: Отправка данных о конверсиях или событиях из CRM в Google Analytics через Measurement Protocol, используя GAS API как промежуточный обработчик.
Безопасность и ограничения
Аутентификация и авторизация (OAuth 2.0)
Если выбрано «Выполнять как: Пользователь, обращающийся к приложению», Apps Script автоматически управляет процессом OAuth 2.0, запрашивая у пользователя разрешение при первом доступе. Для сценариев «Выполнять как: Я» и «Доступ: Все», API является публичным. Для защиты таких эндпоинтов можно реализовать собственные механизмы:
- Секретный токен/ключ API: Передавать секретный ключ в заголовке (
Authorization: Bearer YOUR_SECRET_TOKEN) или параметре запроса и проверять его на стороне сервера. - Проверка IP-адресов: Ограничивать доступ только с определенных IP (менее надежно).
Защита от злоупотреблений и ограничение количества запросов (rate limiting)
Apps Script имеет встроенные квоты на выполнение скриптов, время выполнения, количество вызовов UrlFetchApp и т.д. Для предотвращения злоупотреблений можно реализовать кастомный rate limiting, например, используя CacheService для отслеживания количества запросов с одного IP или по API-ключу за определенный период.
Использование сервиса Script Properties для хранения секретов
Никогда не храните API-ключи, пароли или другие секреты непосредственно в коде. Используйте PropertiesService.getScriptProperties() для безопасного хранения и извлечения конфигурационных данных и секретов.
/**
* Пример проверки API ключа, хранящегося в Script Properties.
* @param {GoogleAppsScript.Events.DoGet | GoogleAppsScript.Events.DoPost} e - Объект события.
* @returns {boolean} - True, если авторизация пройдена.
*/
function checkApiKey(e: GoogleAppsScript.Events.DoGet | GoogleAppsScript.Events.DoPost): boolean {
const scriptProperties = PropertiesService.getScriptProperties();
const storedApiKey = scriptProperties.getProperty('API_KEY');
if (!storedApiKey) {
Logger.log('API_KEY is not set in Script Properties.');
return false; // Не настроено - запрещаем доступ
}
// Ищем ключ в заголовке Authorization: Bearer <key>
let providedKey: string | null = null;
if (e.headers && e.headers.authorization) {
const authHeader = e.headers.authorization;
if (authHeader.toLowerCase().startsWith('bearer ')) {
providedKey = authHeader.substring(7);
}
}
// Или ищем в параметре запроса apiKey=<key>
else if (e.parameter && e.parameter.apiKey) {
providedKey = e.parameter.apiKey;
}
if (!providedKey || providedKey !== storedApiKey) {
Logger.log('Invalid or missing API Key.');
return false;
}
return true;
}
// В doGet/doPost добавить вызов:
// if (!checkApiKey(e)) {
// return ContentService.createTextOutput(JSON.stringify({ success: false, message: 'Unauthorized' }))
// .setMimeType(ContentService.MimeType.JSON);
// }
Ограничения Google Apps Script и способы их обхода
- Время выполнения: Максимум 6 минут для consumer-аккаунтов и 30 минут для Google Workspace.
- Одновременное выполнение: Ограничения на количество одновременно запущенных скриптов.
- Квоты API: Суточные лимиты на вызовы сервисов Google (SpreadsheetApp, DriveApp и т.д.).
- Холодный старт: Первый запуск после периода неактивности может занимать несколько секунд.
- Отсутствие контроля над HTTP-статусом ответа: Всегда 200 или 302.
Способы обхода: Декомпозиция задач, использование триггеров по времени для длительных операций, кэширование (CacheService), оптимизация вызовов API (пакетные операции), использование Cloud Functions для более требовательных задач.