APIキャッシュの重要性
APIのパフォーマンスとコストを最適化する最も効果的な手法の一つがキャッシュです。適切なキャッシュ戦略でレスポンスタイムを短縮し、データベース負荷を削減し、コストを削減できます。
HTTP Cache-Control ヘッダー
// パブリックAPIレスポンスのキャッシュ設定
app.get('/api/products', async (req, res) => {
const products = await getProducts();
res.set({
'Cache-Control': 'public, max-age=300, stale-while-revalidate=60',
// public: CDNとブラウザでキャッシュ可
// max-age=300: 5分間キャッシュ有効
// stale-while-revalidate=60: 有効期限後60秒間は古いキャッシュを返しながらバックグラウンド更新
'ETag': generateETag(products), // コンテンツハッシュ
'Last-Modified': new Date().toUTCString()
});
res.json(products);
});
// 認証必要なデータはprivate
res.set('Cache-Control', 'private, max-age=60');
Redisキャッシュの実装
import { createClient } from 'redis';
const redis = createClient({ url: process.env.REDIS_URL });
await redis.connect();
// キャッシュラッパー関数
const withCache = async (key, ttl, fetchFn) => {
const cached = await redis.get(key);
if (cached) return JSON.parse(cached);
const data = await fetchFn();
await redis.setEx(key, ttl, JSON.stringify(data));
return data;
};
// 使用例
app.get('/api/rankings', async (req, res) => {
const rankings = await withCache(
'api:rankings:top50',
60 * 10, // 10分TTL
() => db.items.findMany({ orderBy: { viewCount: 'desc' }, take: 50 })
);
res.json(rankings);
});
// キャッシュの無効化
const invalidateRankingCache = async () => {
await redis.del('api:rankings:top50');
};
// データ更新後に呼び出す
CDNキャッシュの設定(Cloudflare)
// Cloudflare Workersでのキャッシュコントロール
export default {
async fetch(request, env) {
const cacheUrl = new URL(request.url);
const cacheKey = new Request(cacheUrl.toString(), request);
const cache = caches.default;
// キャッシュを確認
let response = await cache.match(cacheKey);
if (response) {
return new Response(response.body, {
headers: { ...response.headers, 'X-Cache': 'HIT' }
});
}
// オリジンから取得
response = await fetch(request);
if (response.ok) {
// Edge Cacheに保存
const responseToCache = response.clone();
event.waitUntil(cache.put(cacheKey, responseToCache));
}
return new Response(response.body, {
headers: { ...response.headers, 'X-Cache': 'MISS' }
});
}
}
GraphQLのキャッシュ
GraphQLはPOSTリクエストが基本のため、HTTPキャッシュが使えません。Apollo Server・Apollo Clientのキャッシュ機能、またはPersistedQuerysを使ってGETリクエストとして送信してCDNキャッシュを活用します。
キャッシュ設計のベストプラクティス
- 頻繁に変化するデータ:TTLを短く(30秒〜1分)かキャッシュしない
- マスタデータ:TTLを長く(1時間〜1日)、更新時に明示的に無効化
- ユーザー固有データ:Cache-Control: privateまたはユーザーIDをキーに含める
- キャッシュスタンピード対策:大量のリクエストがキャッシュ切れと同時に来る問題を防ぐため、TTLに若干のジッターを加える
まとめ
APIキャッシュはパフォーマンス改善の中で最も費用対効果が高い施策の一つです。HTTP Cache-ControlヘッダーはCDNとブラウザでのキャッシュを制御し、RedisはDBクエリ結果をメモリにキャッシュしてレスポンスを高速化します。データの更新頻度・ユーザー固有性・整合性要件に応じて適切なキャッシュ戦略を選択してください。