学生インターンシップです!
スパイラル株式会社の学生インターンとして、SPIRALを学習しながらユーザー目線で質問に答える活動をしています。
補足情報や調査結果の提供を行い、皆様のお役立てができればと思っています!
※学生が学びの一環として調査・まとめた内容です。
正確な仕様や最新情報は、公式リファレンスなどをご確認ください。
はじめに
SPIRALでインターンをしている学生の私ですが、SPIRALで外部連携に挑戦しようとすると、
「API連携」「Webhook」「Google連携」
などの言葉をよく目にします。
今回は実際に手を動かして、API連携の基礎を学ぶ構築をしてみます!
この構築でやること
- SPIRAL から 外部送信(Webhook) でデータを送る
- 受け取り先として Google Apps Script(GAS) を使う
- GAS を APIサーバーのように機能させる
- 受け取ったデータをログに残す
「API連携の仕組みを理解すること」を目的として、作成してみます!
構成案
↓ 外部送信 / POST
Google Apps Script(疑似API)
↓
スプレッドシートにログ保存
今回GAS の役割は、「SPIRAL と外部サービスの間に立つ中継サーバー」としました。
構築①:データ連携用のスプレッドシートを作成
今回は以下のようなシートを用意しました。
シート名(ここでは「log」)と、以下のシートIDを控えておきます。
https://docs.google.com/spreadsheets/d/【この部分】/edit?gid=0#gid=0
構築②:Google Apps Script の設定
2-1. GAS プロジェクトを作成
A) Google ドライブを開く
B) 新規 → その他 → Google Apps Script
C) プロジェクト名を設定
2-2. Web API として使うための基本コードを入力する
以下のコードを使用しています。
/**
* SPIRAL -> GAS Webhook Receiver (Pseudo API)
* 受信したJSONを検証して、スプレッドシートにログ保存する
*
* 期待するリクエスト(JSON):
* {
* "recordId": "xxxxx",
* "token": "shared_secret",
* "event": "created",
* "sentAt": "2026-01-14T10:00:00Z"
* }
*/
const SPREADSHEET_ID = "構築①で取得したシートID";
const SHEET_NAME = "構築①で作成したシート名";
const SHARED_TOKEN = "my_shared_secret_2026";
function doPost(e) {
try {
const raw = e?.postData?.contents || "";
if (!raw) return json({ status: 400, ok: false, error: "Empty body" });
let data;
try {
data = JSON.parse(raw);
} catch (err) {
return json({ status: 400, ok: false, error: "Invalid JSON", raw });
}
// 簡易認証
if (!data.token || data.token !== SHARED_TOKEN) {
return json({ status: 401, ok: false, error: "Unauthorized" });
}
const recordId = String(data.recordId || "").trim();
if (!recordId) {
return json({ status: 400, ok: false, error: "recordId is missing" });
}
// ログ保存
const sheet = SpreadsheetApp.openById(SPREADSHEET_ID).getSheetByName(SHEET_NAME);
if (!sheet) {
return json({ status: 500, ok: false, error: `Sheet not found: ${SHEET_NAME}` });
}
// 受信ログ:受信時刻 / recordId / event / 生JSON
sheet.appendRow([new Date(), recordId, String(data.event || ""), raw]);
return json({ status: 200, ok: true, recordId });
} catch (err) {
console.error(err);
return json({ status: 500, ok: false, error: String(err) });
}
}
function json(obj) {
return ContentService
.createTextOutput(JSON.stringify(obj))
.setMimeType(ContentService.MimeType.JSON);
}
/**
* 初回権限付与用(必要なら一度実行)
* スプレッドシートへのアクセス権限を先に通す
*/
function authCheck() {
const sheet = SpreadsheetApp.openById(SPREADSHEET_ID).getSheetByName(SHEET_NAME);
if (!sheet) throw new Error("Sheet not found");
return sheet.getLastRow();
}
2-3. Webアプリとして公開する
A) 右上「デプロイ」→「新しいデプロイ」
B) 種類:ウェブアプリ
C) 実行ユーザー:自分
D) アクセス権:全員
E) デプロイ → URL をコピー:「疑似APIサーバーのエンドポイント」
構築③:SPIRAL 側の外部送信設定
SPIRAL の外部送信は、特定のイベントが起きたら指定URLにデータをPOSTします。
今回は、以下のように構築していきます。
- トリガ:フォーム新規登録時、データ更新時
- 送信先URL:GAS の WebアプリURL
3-1. 作成済アプリのDBメニューから、トリガを作成
今回使用するのは非同期アクションです。
3-2. 非同期アクションの設定を行う
3-3. 「+」をクリックし、「PHP実行」を選択
3-4. 表示名や経路条件などを設定し、PHP本文を記述
使用したコードは以下の通りです。
<?php
/**
* SPIRAL ver.2 DBトリガ(登録) -> GAS WebアプリへPOST
* - recordId を最小送信
* - GASの302リダイレクトに追従(Moved Temporarily 対策)
* - レスポンスJSONの成功判定を柔軟に行う
*/
define('GAS_WEBHOOK_URL', '構築②で作成したGASのウェブアプリURL');
define('SHARED_TOKEN', 'my_shared_secret_2026'); // GAS側と一致させる
if (!isset($SPIRAL)) {
throw new Exception('SPIRAL runtime is not available.');
}
/** 1) レコード取得 */
$record = $SPIRAL->getRecord();
$item = $record['item'] ?? null;
if (!is_array($item)) {
throw new Exception('Record is empty.');
}
/** 2) レコードID(通常 _id) */
$recordId = (string)($item['_id'] ?? '');
if ($recordId === '') {
throw new Exception('Record ID is missing.');
}
/** 3) 送信payload(最小+拡張しやすい項目) */
$payload = [
'recordId' => $recordId,
'token' => SHARED_TOKEN,
'event' => 'created',
'sentAt' => date('c'),
];
/** 4) GASへPOST(302追従) */
$ch = curl_init(GAS_WEBHOOK_URL);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload, JSON_UNESCAPED_UNICODE));
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
// ★重要:GASが302を返す場合があるので追従する
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_MAXREDIRS, 5);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$err = curl_error($ch);
curl_close($ch);
if ($response === false) {
throw new Exception('cURL Error: ' . $err);
}
// 2xx以外はエラー(追従後の結果で判定)
if ($httpCode < 200 || $httpCode >= 300) {
throw new Exception("HTTP Error: {$httpCode}, body={$response}");
}
/** 5) レスポンスJSONを確認 */
$decoded = json_decode($response, true);
if (!is_array($decoded)) {
throw new Exception('Invalid JSON response: ' . $response);
}
// 成功判定
$isOk =
(($decoded['ok'] ?? false) === true) ||
((int)($decoded['status'] ?? 0) === 200) ||
(($decoded['status'] ?? null) === 'ok');
if (!$isOk) {
throw new Exception('GAS returned error: ' . $response);
}
3-5. 「保存」をクリックして完了
動作確認をしてみます!
作成した非同期アクションをクリックして、「編集」をクリックします。
コードを記述したテキストボックスの下、「手動実行」をクリックします。
レコードIDを入力して「実行」をクリックすると、アクションが実行されます。
スプレッドシートを確認すると、登録情報が記録されています!
また、このDBで作成したサイトから情報を登録すると、スプレッドシートに登録情報が更新されています!
まとめ
本記事では実際に手を動かして、API連携の基礎を学ぶ構築を試してみました!
コードの記述にはAIの力を借りてもいますが、なんとか思い描いていた動作を実現できてうれしいです!
今回の構築で、APIサーバーがどういったものか実感することができました。
GASでの構築は無料で試せるので、私のような初心の練習場としてかなりおすすめです。
- API = 難しいサーバーではない
- POSTを受けて、JSONを返す
今回はスプレッドシートのデータ更新を目標にしましたが、以下のようにステップアップしてみるとより面白そうです!
- 保存データの整形
- Googleサービスへのデータ連携
- Teams / Slack 連携


















