― $filter=id in (…)
の壁を Chunk 分割+並列制御で突破する実践パターン
1. 背景 ― Entra ID (旧 Azure AD) で大量ユーザーをまとめて取得したい
- 監査や移行時、指定されたユーザー ID 群 (Object ID) を一括で取得したい
例)外部 CSV に 8 000 ID、これを Microsoft Graph で/users
検索
($filter
を使用する際に、Advanced queryにする必要な場合がある。) - Graph の典型的な書き方は
• しかし ID が多すぎると URL 長さ制限の壁 に当たる場合がある。
GET /v1.0/users?$filter=id in ('id-1','id-2',…)
(HTTP/1.1 ゲートウェイで ≒ 2 048 byte を越えると HTTP 414 URI Too Long)
要するに 「1 回で投げられる ID 数は数百が限界」。
単純ループ (8 000 回) では遅すぎるし、8 000 回を並列処理にするとGraph APIのアクセス制限がかかってくる。
つまり、太いクエリ×適度な並列 の設計が必要。
⸻
2. 攻略プラン
ステップ | 目的 |
---|---|
Chunk 分割 – 例:200 ID / Chunk | URL 8 KB とクエリ制限に収める |
並列上限を適切に固定 | Entra ID テナントの同時リクエスト閾値を守る |
⸻
3. TypeScript 実装スニペット
import axios, { AxiosRequestConfig } from 'axios';
import pLimit from 'p-limit';
import axiosRetry from 'axios-retry';
/* ① 429 / 5xx を自動再試行 */
axiosRetry(axios, {
retries: 5,
retryDelay: axiosRetry.exponentialDelay,
retryCondition: err => {
const s = err.response?.status;
return s === 429 || s >= 500;
},
});
/* ② パラメータ */
const CHUNK = 200; // 200 ID / クエリ
const CONCUR = 4; // 4 クエリ同時まで
export async function fetchUsersByIds(token: string, ids: string[]) {
if (!ids.length) return [];
/* Chunk 化 */
const chunks: string[][] = [];
for (let i = 0; i < ids.length; i += CHUNK) {
chunks.push(ids.slice(i, i + CHUNK));
}
/* p-limit で並列数を固定 */
const limit = pLimit(CONCUR);
const tasks = chunks.map(batch =>
limit(() => {
const filter =
`id in (${batch.map(id => `'${id}'`).join(',')})`;
return fetchUsers(token, filter); // ← 既存のページング付き取得関数
}),
);
const pages = await Promise.all(tasks);
return pages.flat(); // 重複 ID がなければこれで完了
}
/* 参考:1 クエリ分を取得 */
async function fetchUsers(token: string, filter: string) {
const users: any[] = [];
let next = 'https://graph.microsoft.com/v1.0/users';
const cfg: AxiosRequestConfig = {
headers: {
Authorization: `Bearer ${token}`,
ConsistencyLevel: 'eventual', // 高度フィルタ必須
},
params: {
$select: 'id,displayName',
$filter: filter,
$count : 'true',
},
};
while (next) {
const { data } = await axios.get(next, cfg);
users.push(...data.value);
next = data['@odata.nextLink'];
}
return users;
}
⸻
まとめ
- 「太いクエリ×適度な並列」でURL長さ制限とAPIアクセス制限を回避つつ、高速にリクエストを実現
- 同じような実装方法で、WEBクローラにも使えるだろう