Google Apps Script 連携

にメンテナンス済み

**Google Apps Script(GAS)**は、Google Workspace のサービスを自動化するためのスクリプト環境です。kintone の REST API と組み合わせることで、Google スプレッドシートと kintone のデータを同期したり、Gmail の受信をトリガーにレコードを作成したりと、さまざまな業務自動化が実現できます。

この記事では、GAS から kintone REST API を呼び出す方法を、認証設定からページネーション対応まで解説します。

事前準備

API トークンの発行

GAS から kintone にアクセスするには、API トークンを使用するのが最も簡単です。

  1. kintone のアプリ設定を開く
  2. 「API トークン」を選択
  3. 「生成する」をクリック
  4. 必要なアクセス権(レコード閲覧、レコード追加、レコード編集など)にチェック
  5. 「保存」→「アプリを更新」
API トークンの取り扱い

API トークンはパスワードと同等の機密情報です。GAS のスクリプトプロパティに保存し、コード上にハードコーディングしないでください。

スクリプトプロパティの設定

GAS エディタの「プロジェクトの設定」→「スクリプト プロパティ」に以下を登録します。

プロパティ名値の例
KINTONE_DOMAINyour-subdomain.cybozu.com
KINTONE_APP_ID123
KINTONE_TOKENxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
setup.gs
/**
 * スクリプトプロパティから設定値を取得する
 * @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 リクエストを送信します。

get-records.gs
/**
 * 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}`);
  });
};
レコードの取得(複数件)
kintone REST APIを使用して、レコードを複数件取得する方法を紹介します。レコードIDを指定して、レコード情報を取得することができます。

500 件を超えるレコードの取得(ページネーション)

kintone の REST API は 1 回のリクエストで最大 500 件のレコードしか取得できません。大量のレコードを取得するにはページネーションを実装する必要があります。

get-all-records.gs
/**
 * ページネーション対応で全レコードを取得する
 * @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;
};
Seek メソッドを使用

この実装では $id を使った Seek メソッドでページネーションを行っています。offset パラメータは 10,000 件が上限のため、大量データの取得には $id を使った方法が推奨されます。

レコードIDを使った一括取得
kintoneにはAPIを使ってレコード情報を取得する方法が複数用意されています。今回はその中でもシーク法と呼ばれる、レコードIDをもとに一括取得する方法を紹介します。

kintone にレコードを登録する

1 件ずつ登録する

add-record.gs
/**
 * 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 件のレコードを登録できます。

add-records-bulk.gs
/**
 * 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;
};
レコードの追加(複数件)
kintone REST APIを使用して、レコードを複数件追加する方法を紹介します。Node.jsやGASでのサンプルコードも掲載しています。

Google スプレッドシートとの連携

スプレッドシートから kintone にデータを登録する

スプレッドシートの内容を kintone のレコードとして登録する実装例です。

spreadsheet-to-kintone.gs
/**
 * アクティブなスプレッドシートの内容を 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-to-spreadsheet.gs
/**
 * 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リクエストパラメータの不正フィールドコードや値の形式を確認
403API トークンの権限不足トークンのアクセス権を確認
404アプリ ID やドメインの間違いURL とアプリ ID を確認
429API のレート制限に到達リクエスト間隔を空ける
520kintone サーバーの一時的なエラー時間をおいてリトライ
retry-fetch.gs
/**
 * リトライ付きの 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 の実行時間制限

GAS には 1 回の実行で 6 分(Google Workspace の場合は 30 分)の時間制限があります。大量のレコードを処理する場合は、処理を複数回に分割するか、続きの処理をトリガーで再開する仕組みを検討してください。

まとめ

  • GAS から kintone REST API を呼び出す場合は UrlFetchApp.fetch と API トークン認証を使用する
  • API トークンはスクリプトプロパティに保存し、コード上にハードコーディングしない
  • 500 件を超えるレコードの取得には $id を使った Seek メソッドでページネーションを実装する
  • 一括登録は 100 件ずつ分割して送信する
  • GAS のトリガー機能を使えば、定期的なデータ同期を自動化できる
  • エラーハンドリングとリトライ機構を実装してリクエストの信頼性を確保する

練習問題

GAS から kintone REST API にアクセスする際、認証ヘッダーとして正しいものはどれですか?

kintone REST API でレコードを一括登録する場合、1 回のリクエストで登録できる最大件数はいくつですか?

GAS で API トークンなどの機密情報を安全に管理する方法として推奨されるのはどれですか?

#kintone #JavaScript #Google Apps Script