kintone.proxyの使い方

にメンテナンス済み

kintone の JavaScript カスタマイズから外部の Web サービスや API にリクエストを送るには、kintone.proxy メソッドを使用します。ブラウザの Same-Origin Policy(同一オリジンポリシー)の制約を受けずに、kintone のサーバーを経由して外部 API を呼び出すことができます。

この記事では、kintone.proxy の基本的な使い方から、実践的な活用方法まで詳しく解説します。

kintone.proxy とは

通常、ブラウザ上の JavaScript から外部ドメインの API を呼び出す場合、CORS(Cross-Origin Resource Sharing)の制約を受けます。しかし、kintone.proxy を使うと、kintone のサーバーが中継役となってリクエストを送信するため、CORS の問題を回避できます。

ブラウザ → kintone サーバー(中継) → 外部 API
チェック

kintone.proxy で送信されたリクエストは、kintone のサーバーから送信されます。そのため、外部 API 側では kintone サーバーの IP アドレスからのアクセスとして処理されます。

基本的な構文

基本構文
kintone.proxy(url, method, headers, body)
  .then((args) => {
    const [body, statusCode, headers] = args;
    console.log(statusCode, body, headers);
  })
  .catch((error) => {
    console.error(error);
  });

パラメータ

パラメータ説明
urlstringリクエスト先の URL
methodstringHTTP メソッド(GET, POST, PUT, DELETE
headersobjectリクエストヘッダー
bodystring | objectリクエストボディ(GET の場合は {} を指定)

レスポンス

kintone.proxy は Promise を返します。成功時には配列で 3 つの値を受け取ります。

インデックス内容説明
[0]bodyHTTP レスポンスボディ(文字列)
[1]statusCodeHTTP ステータスコード(数値)
[2]headersレスポンスヘッダー(オブジェクト)

GET リクエスト

外部 API からデータを取得する基本的な例です。

proxy-get.js
(() => {
  'use strict';

  /**
   * 外部APIからデータを取得する
   * @param { string } url - リクエスト先のURL
   * @param { Object } headers - リクエストヘッダー
   * @returns { Promise<any> } パース済みのレスポンスデータ
   */
  const fetchFromApi = async (url, headers = {}) => {
    const [body, statusCode] = await kintone.proxy(url, 'GET', headers, {});

    if (statusCode !== 200) {
      throw new Error(`APIリクエストが失敗しました: ${statusCode}`);
    }

    return JSON.parse(body);
  };

  kintone.events.on('app.record.index.show', async (event) => {
    try {
      const data = await fetchFromApi('https://api.example.com/data', {
        Authorization: 'Bearer your-api-token',
      });
      console.log('取得したデータ:', data);
    } catch (error) {
      console.error('データ取得に失敗しました:', error);
    }

    return event;
  });
})();

POST リクエスト

外部 API にデータを送信する例です。

proxy-post.js
(() => {
  'use strict';

  /**
   * 外部APIにデータを送信する
   * @param { string } url - リクエスト先のURL
   * @param { Object } data - 送信するデータ
   * @param { Object } headers - リクエストヘッダー
   * @returns { Promise<any> } パース済みのレスポンスデータ
   */
  const postToApi = async (url, data, headers = {}) => {
    const requestHeaders = {
      'Content-Type': 'application/json',
      ...headers,
    };

    const [body, statusCode] = await kintone.proxy(
      url,
      'POST',
      requestHeaders,
      JSON.stringify(data)
    );

    if (statusCode < 200 || statusCode >= 300) {
      throw new Error(`APIリクエストが失敗しました: ${statusCode} - ${body}`);
    }

    return JSON.parse(body);
  };

  kintone.events.on('app.record.detail.show', async (event) => {
    // ヘッダスペースにボタンを設置
    const button = document.createElement('button');
    button.textContent = '外部システムに送信';
    button.onclick = async () => {
      try {
        const record = kintone.app.record.get().record;
        const result = await postToApi(
          'https://api.example.com/items',
          {
            title: record['タイトル'].value,
            description: record['説明'].value,
          },
          { Authorization: 'Bearer your-api-token' }
        );
        alert('送信が完了しました');
        console.log(result);
      } catch (error) {
        alert('送信に失敗しました');
        console.error(error);
      }
    };

    kintone.app.record.getHeaderMenuSpaceElement().append(button);

    return event;
  });
})();

Content-Type による body の指定方法

kintone.proxy に渡す body の形式は、Content-Type ヘッダーの値によって異なります。

JSON 形式の場合

content-type-json.js
const headers = { 'Content-Type': 'application/json' };
const body = JSON.stringify({ key: 'value' });

kintone.proxy(url, 'POST', headers, body);

フォームデータ形式の場合

content-type-form.js
const headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
const body = new URLSearchParams({ key: 'value' }).toString();

kintone.proxy(url, 'POST', headers, body);
チェック

Content-Typebody の形式が一致しない場合、外部 API 側でリクエストが正しく解釈されない可能性があります。API のドキュメントを確認して、適切な形式を使用してください。

エラーハンドリング

kintone.proxy のエラーハンドリングでは、ネットワークエラーと HTTP エラーの両方を考慮する必要があります。

error-handling.js
/**
 * kintone.proxy のラッパー関数(エラーハンドリング付き)
 * @param { string } url - リクエスト先のURL
 * @param { string } method - HTTPメソッド
 * @param { Object } headers - リクエストヘッダー
 * @param { string | Object } body - リクエストボディ
 * @returns { Promise<{ body: any; statusCode: number; headers: Object }> }
 */
const safeProxy = async (url, method, headers, body) => {
  try {
    const [responseBody, statusCode, responseHeaders] = await kintone.proxy(
      url,
      method,
      headers,
      body
    );

    // HTTPステータスコードが400以上の場合はエラーとして扱う
    if (statusCode >= 400) {
      const errorMessage = (() => {
        try {
          return JSON.parse(responseBody).message || responseBody;
        } catch {
          return responseBody;
        }
      })();
      throw new Error(`HTTP ${statusCode}: ${errorMessage}`);
    }

    return {
      body: (() => {
        try {
          return JSON.parse(responseBody);
        } catch {
          return responseBody;
        }
      })(),
      statusCode,
      headers: responseHeaders,
    };
  } catch (error) {
    // kintone.proxy 自体のエラー(ネットワークエラー等)
    console.error('kintone.proxy でエラーが発生しました:', error);
    throw error;
  }
};
チェック

kintone.proxy は HTTP ステータスコードが 400 以上でも Promise を reject しません。ステータスコードは正常に返されるため、自分でステータスコードを確認してエラー処理を行う必要があります。

制限事項

kintone.proxy にはいくつかの制限事項があります。

制限項目内容
リクエストサイズリクエストボディの上限は 10 MB
レスポンスサイズレスポンスボディの上限は 10 MB
タイムアウト外部 API からのレスポンスが返るまでの制限時間は 60 秒
同時リクエスト数同一ドメインへの同時リクエスト数に上限あり
HTTPSリクエスト先の URL は HTTPS のみ対応
チェック

kintone.proxy は HTTPS の URL のみサポートしています。HTTP(非暗号化)の URL にはリクエストを送信できません。

kintone.proxy.upload

ファイルを外部サービスにアップロードする場合は、kintone.proxy.upload を使用します。

proxy-upload.js
// Blobオブジェクトを作成
const blob = new Blob(['ファイルの内容'], { type: 'text/plain' });

kintone.proxy.upload(
  'https://api.example.com/upload',
  'POST',
  { Authorization: 'Bearer your-api-token' },
  { format: 'RAW', value: blob }
).then((args) => {
  const [body, statusCode] = args;
  console.log('アップロード結果:', statusCode, body);
});

実践例: 外部 API のデータを kintone レコードに反映する

外部の住所検索 API を使って、郵便番号から住所を自動入力する例です。

zipcode-lookup.js
(() => {
  'use strict';

  /**
   * 郵便番号から住所を検索する
   * @param { string } zipCode - 郵便番号(ハイフンなし7桁)
   * @returns { Promise<{ address1: string; address2: string; address3: string } | null> }
   */
  const searchAddress = async (zipCode) => {
    const url = `https://zipcloud.ibsnet.co.jp/api/search?zipcode=${zipCode}`;
    const [body, statusCode] = await kintone.proxy(url, 'GET', {}, {});

    if (statusCode !== 200) {
      return null;
    }

    const data = JSON.parse(body);
    if (!data.results || data.results.length === 0) {
      return null;
    }

    return data.results[0];
  };

  // 郵便番号が変更されたら住所を自動入力
  const events = [
    'app.record.create.change.郵便番号',
    'app.record.edit.change.郵便番号',
  ];

  kintone.events.on(events, (event) => {
    const record = event.record;
    const zipCode = (record['郵便番号'].value || '').replace(/-/g, '');

    if (zipCode.length !== 7) {
      return event;
    }

    // 注意: change イベントでは async/await は使えないため、
    // 住所の自動入力はボタンクリックなど別のトリガーで実装するのが確実です
    return event;
  });
})();
チェック

上記の例では郵便番号検索 API(zipcloud)を使用しています。change イベントでは非同期処理がサポートされていないため、実際のプロダクションコードではボタンクリックや submit イベントで住所検索を実行する設計が推奨されます。

Chatwork - メッセージの送信
引数に各パラメータを設定することで利用できます。返り値が Promise オブジェクトなので、送信を確認してから処理したい場合は以下のような記述が必要です。今回はメッセージを送りたいだけなので、POST を使用します。

まとめ

  • kintone.proxy を使うことで、CORS の制約を受けずに外部 API を呼び出せる
  • レスポンスは [body, statusCode, headers] の配列で返される
  • HTTP ステータスコードが 400 以上でも Promise は reject されないため、自分でエラー処理を行う必要がある
  • リクエストサイズ・レスポンスサイズ・タイムアウトに上限がある
  • HTTPS の URL のみサポートされている
  • ファイルアップロードには kintone.proxy.upload を使用する
#kintone #JavaScript #TypeScript