bulkRequest
kintone REST API の bulkRequest は、複数の API リクエストを 1 回のリクエストにまとめて送信できる機能です。通常の REST API では操作ごとに個別のリクエストが必要ですが、bulkRequest を使えば最大 20 件のリクエストを一括で実行でき、しかもすべての操作がアトミック(全成功 or 全失敗)に処理されます。
この記事では、bulkRequest の基本的な使い方から、実践的な活用パターンまで解説します。
bulkRequest とは
通常の API との違い
| 項目 | 通常の REST API | bulkRequest |
|---|---|---|
| リクエスト数 | 操作ごとに 1 リクエスト | 最大 20 操作を 1 リクエスト |
| 原子性 | なし(個別に成功/失敗) | あり(全成功 or 全失敗) |
| エンドポイント | 各操作に対応する URL | /k/v1/bulkRequest.json |
| 対応操作 | - | POST/PUT/DELETE 系のみ(GET 不可) |
| 1 操作あたり上限 | 100 件 | 100 件(通常と同じ) |
bulkRequest 内のいずれかのリクエストがエラーになった場合、すべてのリクエストがロールバックされます。データの整合性が重要な場面で特に有効です。
対応する API
bulkRequest で実行できる API は以下の通りです。
| API | メソッド | エンドポイント | 上限 |
|---|---|---|---|
| レコード追加(1 件) | POST | /k/v1/record.json | - |
| レコード追加(複数件) | POST | /k/v1/records.json | 100 件 |
| レコード更新(1 件) | PUT | /k/v1/record.json | - |
| レコード更新(複数件) | PUT | /k/v1/records.json | 100 件 |
| レコード削除 | DELETE | /k/v1/records.json | 100 件 |
| ステータス変更 | PUT | /k/v1/record/status.json | - |
| ステータス一括変更 | PUT | /k/v1/records/status.json | 100 件 |
GET 系の API(レコード取得、アプリ情報取得など)は bulkRequest で実行できません。bulkRequest は書き込み系の操作のみに対応しています。
基本的な使い方
kintone から利用する場合
(() => {
'use strict';
/**
* bulkRequest を実行する
* @param { Object[] } requests - 実行するリクエストの配列
* @returns { Promise<{ results: Object[] }> }
*/
const executeBulkRequest = (requests) => {
return kintone.api(kintone.api.url('/k/v1/bulkRequest.json', true), 'POST', {
requests,
});
};
// 使用例: レコードの追加と更新を同時に実行
const events = ['app.record.detail.show'];
kintone.events.on(events, async (event) => {
const appId = kintone.app.getId();
const requests = [
{
method: 'POST',
api: '/k/v1/records.json',
payload: {
app: appId,
records: [
{ '案件名': { value: '新規案件A' }, 'ステータス': { value: '未着手' } },
{ '案件名': { value: '新規案件B' }, 'ステータス': { value: '未着手' } },
],
},
},
{
method: 'PUT',
api: '/k/v1/records.json',
payload: {
app: appId,
records: [
{ id: 1, record: { 'ステータス': { value: '完了' } } },
{ id: 2, record: { 'ステータス': { value: '完了' } } },
],
},
},
];
try {
const { results } = await executeBulkRequest(requests);
console.log('bulkRequest 成功:', results);
} catch (error) {
console.error('bulkRequest エラー:', error);
}
return event;
});
})();
bulkRequest 内の api
プロパティにはゲストスペース用のパスも指定できます。ゲストスペース内のアプリの場合は /k/guest/ {スペースID}/v1/records.json のように指定してください。
Node.js から利用する場合
const fetch = require('node-fetch');
const DOMAIN = 'your-subdomain.cybozu.com';
const TOKEN = 'your-api-token';
/**
* bulkRequest を実行する
* @param { Object[] } requests - 実行するリクエストの配列
* @returns { Promise<{ results: Object[] }> }
*/
const executeBulkRequest = async (requests) => {
const response = await fetch(`https://${DOMAIN}/k/v1/bulkRequest.json`, {
method: 'POST',
headers: {
'X-Cybozu-API-Token': TOKEN,
'Content-Type': 'application/json',
},
body: JSON.stringify({ requests }),
});
if (!response.ok) {
const error = await response.json();
throw new Error(JSON.stringify(error));
}
return response.json();
};
実践パターン
パターン 1: アプリ間のデータ同期
あるアプリのレコードを別のアプリにコピーし、元のアプリのステータスを更新する処理です。bulkRequest を使うことで、コピー失敗時にステータスだけ更新される不整合を防ぎます。
(() => {
'use strict';
/**
* レコードを別アプリにコピーし、元アプリのステータスを更新する
* @param { Object } params
* @param { number } params.sourceAppId - コピー元アプリID
* @param { number } params.targetAppId - コピー先アプリID
* @param { Object[] } params.records - コピーするレコード
*/
const syncRecords = async (params) => {
const { sourceAppId, targetAppId, records } = params;
// コピー先に追加するレコード
const newRecords = records.map((record) => ({
'案件名': { value: record['案件名'].value },
'金額': { value: record['金額'].value },
'元アプリレコードID': { value: record['$id'].value },
}));
// 元アプリのステータスを更新
const updateRecords = records.map((record) => ({
id: record['$id'].value,
record: {
'コピー済み': { value: ['済'] },
},
}));
const requests = [
{
method: 'POST',
api: '/k/v1/records.json',
payload: { app: targetAppId, records: newRecords },
},
{
method: 'PUT',
api: '/k/v1/records.json',
payload: { app: sourceAppId, records: updateRecords },
},
];
return kintone.api(kintone.api.url('/k/v1/bulkRequest.json', true), 'POST', {
requests,
});
};
})();
パターン 2: 安全なデリートインサート
古いデータを削除してから新しいデータを登録する処理を、bulkRequest でアトミックに実行します。
(() => {
'use strict';
/**
* 指定条件のレコードを削除してから新しいレコードを登録する
* @param { Object } params
* @param { number } params.app - アプリID
* @param { number[] } params.deleteIds - 削除するレコードIDの配列
* @param { Object[] } params.newRecords - 登録する新しいレコードの配列
*/
const deleteAndInsert = async (params) => {
const { app, deleteIds, newRecords } = params;
const requests = [];
// 削除リクエスト(100 件ずつ分割)
for (let i = 0; i < deleteIds.length; i += 100) {
const chunk = deleteIds.slice(i, i + 100);
requests.push({
method: 'DELETE',
api: '/k/v1/records.json',
payload: { app, ids: chunk },
});
}
// 追加リクエスト(100 件ずつ分割)
for (let i = 0; i < newRecords.length; i += 100) {
const chunk = newRecords.slice(i, i + 100);
requests.push({
method: 'POST',
api: '/k/v1/records.json',
payload: { app, records: chunk },
});
}
// bulkRequest は最大 20 リクエスト
if (requests.length > 20) {
throw new Error(
`bulkRequest の上限(20)を超えています: ${requests.length} リクエスト`
);
}
return kintone.api(kintone.api.url('/k/v1/bulkRequest.json', true), 'POST', {
requests,
});
};
})();
パターン 3: ステータスの一括変更
複数レコードのプロセス管理ステータスを一括で変更する処理です。
(() => {
'use strict';
/**
* 複数レコードのステータスを一括変更する
* @param { Object } params
* @param { number } params.app - アプリID
* @param { Object[] } params.statusUpdates - ステータス変更情報の配列
*/
const bulkUpdateStatus = async (params) => {
const { app, statusUpdates } = params;
// 100 件ずつ分割
const requests = [];
for (let i = 0; i < statusUpdates.length; i += 100) {
const chunk = statusUpdates.slice(i, i + 100);
requests.push({
method: 'PUT',
api: '/k/v1/records/status.json',
payload: {
app,
records: chunk.map((item) => ({
id: item.id,
action: item.action,
assignee: item.assignee,
})),
},
});
}
return kintone.api(kintone.api.url('/k/v1/bulkRequest.json', true), 'POST', {
requests,
});
};
// 使用例
// bulkUpdateStatus({
// app: 123,
// statusUpdates: [
// { id: 1, action: '承認する', assignee: 'user1' },
// { id: 2, action: '承認する', assignee: 'user1' },
// ],
// });
})();
エラーハンドリング
bulkRequest でエラーが発生した場合、レスポンスにはどのリクエストでエラーが発生したかの情報が含まれます。
try {
const { results } = await kintone.api(
kintone.api.url('/k/v1/bulkRequest.json', true),
'POST',
{ requests }
);
console.log('成功:', results);
} catch (error) {
// error.message にはエラーの詳細が含まれる
console.error('bulkRequest エラー:', error.message);
// エラーレスポンスの例:
// {
// "results": [
// {}, // 1 番目のリクエストは問題なし
// { // 2 番目のリクエストでエラー
// "message": "不正なフィールドが...",
// "id": "xxxxx",
// "code": "CB_VA01"
// }
// ]
// }
}
CB_VA01 は必須フィールドの未入力、CB_NO02 は権限不足、GAIA_RE18
はリクエスト数の上限超過を示します。エラーコードに応じた適切なメッセージをユーザーに表示してください。
制限事項
bulkRequest を使用する際の制限事項を整理します。
| 制限項目 | 上限値 |
|---|---|
| 1 回の bulkRequest のリクエスト数 | 最大 20 件 |
| 各リクエスト内のレコード数 | 最大 100 件(通常の API と同じ) |
| GET 系 API の使用 | 不可 |
| アプリの横断 | 可能(異なるアプリの操作を混在可) |
| ゲストスペース | api パスにスペース ID を含めれば可 |
| API トークン | 全リクエストで使用するアプリの権限が必要 |
bulkRequest で複数のアプリを操作する場合、すべてのアプリに対するアクセス権を持つ API
トークンが必要です。異なるアプリの API トークンをカンマ区切りで指定することも可能です(例:
X-Cybozu-API-Token: token1, token2)。
まとめ
- bulkRequest は最大 20 件の書き込み系 API リクエストを 1 回のリクエストで一括実行できる
- すべての操作がアトミックに処理されるため、一部だけ成功する不整合を防げる
- アプリ間のデータ同期、デリートインサート、ステータスの一括変更などに有効
- GET 系の API は bulkRequest では使用できない
- 各リクエスト内のレコード数は通常と同じく最大 100 件
- 複数アプリを操作する場合は、全アプリのアクセス権を持つ API トークンが必要