Google Apps Script 連携
**Google Apps Script(GAS)**は、Google Workspace のサービスを自動化するためのスクリプト環境です。kintone の REST API と組み合わせることで、Google スプレッドシートと kintone のデータを同期したり、Gmail の受信をトリガーにレコードを作成したりと、さまざまな業務自動化が実現できます。
この記事では、GAS から kintone REST API を呼び出す方法を、認証設定からページネーション対応まで解説します。
事前準備
API トークンの発行
GAS から kintone にアクセスするには、API トークンを使用するのが最も簡単です。
- kintone のアプリ設定を開く
- 「API トークン」を選択
- 「生成する」をクリック
- 必要なアクセス権(レコード閲覧、レコード追加、レコード編集など)にチェック
- 「保存」→「アプリを更新」
API トークンはパスワードと同等の機密情報です。GAS のスクリプトプロパティに保存し、コード上にハードコーディングしないでください。
スクリプトプロパティの設定
GAS エディタの「プロジェクトの設定」→「スクリプト プロパティ」に以下を登録します。
| プロパティ名 | 値の例 |
|---|---|
KINTONE_DOMAIN | your-subdomain.cybozu.com |
KINTONE_APP_ID | 123 |
KINTONE_TOKEN | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
/**
* スクリプトプロパティから設定値を取得する
* @returns {{ domain: string, appId: string, token: string }}
*/
const getConfig = () => {
const props = PropertiesService.getScriptProperties();
return {
domain: props.getProperty('KINTONE_DOMAIN'),
appId: props.getProperty('KINTONE_APP_ID'),
token: props.getProperty('KINTONE_TOKEN'),
};
};
kintone からレコードを取得する
基本的な取得
GAS では UrlFetchApp.fetch を使って HTTP リクエストを送信します。
/**
* kintone REST API を使用してレコードを取得する
* @param { Object } params
* @param { string } params.domain - kintone のドメイン
* @param { string | number } params.app - アプリID
* @param { string } params.token - API トークン
* @param { string } [params.query] - 検索クエリ
* @param { string[] } [params.fields] - 取得するフィールド
* @returns { { records: Object[] } }
*/
const getRecords = (params) => {
const { domain, app, token, query, fields } = params;
const queryParams = new URLSearchParams();
queryParams.append('app', app);
if (query) queryParams.append('query', query);
if (fields) fields.forEach((f) => queryParams.append('fields[0]', f));
const url = `https://${domain}/k/v1/records.json?${queryParams.toString()}`;
const response = UrlFetchApp.fetch(url, {
method: 'GET',
headers: {
'X-Cybozu-API-Token': token,
},
muteHttpExceptions: true,
});
if (response.getResponseCode() !== 200) {
throw new Error(`kintone API エラー: ${response.getContentText()}`);
}
return JSON.parse(response.getContentText());
};
const main = () => {
const config = getConfig();
const { records } = getRecords({
domain: config.domain,
app: config.appId,
token: config.token,
query: 'ステータス in ("未対応", "対応中") order by 更新日時 desc',
});
records.forEach((record) => {
Logger.log(`${record['案件名'].value}: ${record['担当者'].value}`);
});
};
500 件を超えるレコードの取得(ページネーション)
kintone の REST API は 1 回のリクエストで最大 500 件のレコードしか取得できません。大量のレコードを取得するにはページネーションを実装する必要があります。
/**
* ページネーション対応で全レコードを取得する
* @param { Object } params
* @param { string } params.domain - kintone のドメイン
* @param { string | number } params.app - アプリID
* @param { string } params.token - API トークン
* @param { string } [params.query] - 検索クエリ(order by / limit / offset は自動付与)
* @returns { Object[] } 全レコードの配列
*/
const getAllRecords = (params) => {
const { domain, app, token, query } = params;
const allRecords = [];
let lastId = 0;
while (true) {
const conditionQuery = query ? `(${query}) and ` : '';
const paginationQuery = `${conditionQuery}$id > ${lastId} order by $id asc limit 500`;
const { records } = getRecords({
domain,
app,
token,
query: paginationQuery,
});
allRecords.push(...records);
if (records.length < 500) break;
lastId = Number(records[records.length - 1]['$id'].value);
}
return allRecords;
};
この実装では $id を使った Seek メソッドでページネーションを行っています。offset パラメータは
10,000 件が上限のため、大量データの取得には $id を使った方法が推奨されます。
kintone にレコードを登録する
1 件ずつ登録する
/**
* kintone にレコードを 1 件追加する
* @param { Object } params
* @param { string } params.domain - kintone のドメイン
* @param { string | number } params.app - アプリID
* @param { string } params.token - API トークン
* @param { Object } params.record - 登録するレコードデータ
* @returns { { id: string, revision: string } }
*/
const addRecord = (params) => {
const { domain, app, token, record } = params;
const url = `https://${domain}/k/v1/record.json`;
const response = UrlFetchApp.fetch(url, {
method: 'POST',
headers: {
'X-Cybozu-API-Token': token,
'Content-Type': 'application/json',
},
payload: JSON.stringify({ app, record }),
muteHttpExceptions: true,
});
if (response.getResponseCode() !== 200) {
throw new Error(`kintone API エラー: ${response.getContentText()}`);
}
return JSON.parse(response.getContentText());
};
100 件単位で一括登録する
一括登録 API は 1 回のリクエストで最大 100 件のレコードを登録できます。
/**
* kintone にレコードを一括追加する(100 件単位で分割)
* @param { Object } params
* @param { string } params.domain - kintone のドメイン
* @param { string | number } params.app - アプリID
* @param { string } params.token - API トークン
* @param { Object[] } params.records - 登録するレコードデータの配列
* @returns { { ids: string[], revisions: string[] }[] }
*/
const addRecordsBulk = (params) => {
const { domain, app, token, records } = params;
const results = [];
// 100 件ずつ分割して送信
for (let i = 0; i < records.length; i += 100) {
const chunk = records.slice(i, i + 100);
const url = `https://${domain}/k/v1/records.json`;
const response = UrlFetchApp.fetch(url, {
method: 'POST',
headers: {
'X-Cybozu-API-Token': token,
'Content-Type': 'application/json',
},
payload: JSON.stringify({ app, records: chunk }),
muteHttpExceptions: true,
});
if (response.getResponseCode() !== 200) {
throw new Error(
`kintone API エラー (${i + 1}〜${i + chunk.length}件目): ${response.getContentText()}`
);
}
results.push(JSON.parse(response.getContentText()));
}
return results;
};
Google スプレッドシートとの連携
スプレッドシートから kintone にデータを登録する
スプレッドシートの内容を kintone のレコードとして登録する実装例です。
/**
* アクティブなスプレッドシートの内容を kintone に登録する
*/
const importFromSpreadsheet = () => {
const config = getConfig();
const sheet = SpreadsheetApp.getActiveSheet();
const data = sheet.getDataRange().getValues();
// 1 行目をヘッダーとして使用
const headers = data[0];
const rows = data.slice(1);
// スプレッドシートの列と kintone のフィールドコードのマッピング
const fieldMapping = {
'案件名': 0, // A 列
'担当者名': 1, // B 列
'金額': 2, // C 列
'ステータス': 3, // D 列
'備考': 4, // E 列
};
const records = rows
.filter((row) => row[0]) // 空行を除外
.map((row) => {
const record = {};
for (const [fieldCode, colIndex] of Object.entries(fieldMapping)) {
record[fieldCode] = { value: String(row[colIndex]) };
}
return record;
});
if (records.length === 0) {
Logger.log('登録するデータがありません');
return;
}
const results = addRecordsBulk({
domain: config.domain,
app: config.appId,
token: config.token,
records,
});
Logger.log(`${records.length} 件のレコードを登録しました`);
};
kintone のデータをスプレッドシートに出力する
/**
* kintone のレコードをスプレッドシートに出力する
*/
const exportToSpreadsheet = () => {
const config = getConfig();
const records = getAllRecords({
domain: config.domain,
app: config.appId,
token: config.token,
query: 'ステータス in ("対応中")',
});
if (records.length === 0) {
Logger.log('対象レコードがありません');
return;
}
// 出力するフィールドの定義
const fields = ['$id', '案件名', '担当者名', '金額', 'ステータス', '更新日時'];
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('kintone_data')
|| SpreadsheetApp.getActiveSpreadsheet().insertSheet('kintone_data');
// シートをクリア
sheet.clear();
// ヘッダーの出力
sheet.getRange(1, 1, 1, fields.length).setValues([fields]);
// データの出力
const data = records.map((record) =>
fields.map((field) => record[field]?.value ?? '')
);
if (data.length > 0) {
sheet.getRange(2, 1, data.length, fields.length).setValues(data);
}
Logger.log(`${records.length} 件のレコードを出力しました`);
};
GAS にはトリガー機能があり、時間主導型のトリガーを設定することで、定期的な同期処理を自動化できます。たとえば「毎日 9 時に kintone のデータをスプレッドシートに出力する」といったスケジュール実行が可能です。
エラーハンドリングとリトライ
GAS から kintone API を呼び出す際に発生しやすいエラーと対処法をまとめます。
| HTTP ステータス | 原因 | 対処法 |
|---|---|---|
| 400 | リクエストパラメータの不正 | フィールドコードや値の形式を確認 |
| 403 | API トークンの権限不足 | トークンのアクセス権を確認 |
| 404 | アプリ ID やドメインの間違い | URL とアプリ ID を確認 |
| 429 | API のレート制限に到達 | リクエスト間隔を空ける |
| 520 | kintone サーバーの一時的なエラー | 時間をおいてリトライ |
/**
* リトライ付きの UrlFetchApp.fetch ラッパー
* @param { string } url - リクエスト先 URL
* @param { Object } options - fetch オプション
* @param { number } [maxRetries=3] - 最大リトライ回数
* @returns { GoogleAppsScript.URL_Fetch.HTTPResponse }
*/
const fetchWithRetry = (url, options, maxRetries = 3) => {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
const response = UrlFetchApp.fetch(url, { ...options, muteHttpExceptions: true });
const statusCode = response.getResponseCode();
if (statusCode === 200) return response;
// リトライ対象のステータスコード
if ([429, 500, 502, 503, 520].includes(statusCode) && attempt < maxRetries) {
const waitTime = Math.pow(2, attempt) * 1000; // 指数バックオフ
Logger.log(`リトライ ${attempt}/${maxRetries}: ${waitTime}ms 待機`);
Utilities.sleep(waitTime);
continue;
}
throw new Error(`kintone API エラー (${statusCode}): ${response.getContentText()}`);
}
};
GAS には 1 回の実行で 6 分(Google Workspace の場合は 30 分)の時間制限があります。大量のレコードを処理する場合は、処理を複数回に分割するか、続きの処理をトリガーで再開する仕組みを検討してください。
まとめ
- GAS から kintone REST API を呼び出す場合は
UrlFetchApp.fetchと API トークン認証を使用する - API トークンはスクリプトプロパティに保存し、コード上にハードコーディングしない
- 500 件を超えるレコードの取得には
$idを使った Seek メソッドでページネーションを実装する - 一括登録は 100 件ずつ分割して送信する
- GAS のトリガー機能を使えば、定期的なデータ同期を自動化できる
- エラーハンドリングとリトライ機構を実装してリクエストの信頼性を確保する