※本記事には広告(A8・楽天アフィリエイト)リンクが含まれる場合があります。
「APIにキャッシュを入れたい」「レート制限を実装したい」「でもRedisサーバーを立てるのは面倒くさい」——個人開発者なら一度は感じたことがあるジレンマではないでしょうか。
そこで登場するのが Upstash です。Upstashはサーバーレス・ペイパーユース型のRedisサービスで、インフラ管理ゼロ・無料枠あり・Cloudflare Workersとの相性抜群という、個人開発者にとって夢のような組み合わせを実現します。
本記事では、Upstash Redisのセットアップから、キャッシュ実装・APIレート制限・セッション管理まで、実際に動くコード例を交えながら解説します。
Upstashとは? なぜ個人開発者に最適なのか
Upstashは2021年に登場した、HTTP REST APIベースのサーバーレスRedisサービスです。通常のRedisはTCP接続が必要ですが、UpstashはHTTP経由でアクセスできるため、Cloudflare WorkersやVercel Edge Functionsなどのサーバーレス環境でも問題なく使えます。
Upstashの料金体系:個人開発者に嬉しい無料枠
| プラン | 料金 | デイリーコマンド数 | 最大データサイズ |
|---|---|---|---|
| Free | $0 | 10,000回/日 | 256MB |
| Pay as you go | $0.2/10万コマンド | 無制限 | 〜100GB |
| Pro 2K | $10/月〜 | 無制限 | 10GB〜 |
個人開発の初期フェーズなら無料枠で十分です。月数千〜数万アクセス程度なら無料でずっと使えます。スケールしてきたらペイパーユースで移行できるのも安心です。
Cloudflare Workers KVとの違い:何を使うべき?
「Cloudflare Workers KVがあるのに、なぜUpstashが必要なの?」という疑問を持つ方も多いでしょう。以下の表で比較します。
| 機能 | Cloudflare KV | Upstash Redis |
|---|---|---|
| データ構造 | Key-Valueのみ | 文字列・Hash・List・Set・Sorted Set |
| 書き込み反映 | 最大60秒の遅延 | 即時 |
| INCR / カウンター | ❌ 非対応 | ✅ 対応 |
| TTL設定 | ✅ 対応 | ✅ 対応 |
| レート制限ライブラリ | ❌ なし | ✅ @upstash/ratelimit |
| Cloudflare以外でも使える | ❌(CF専用) | ✅ Vercel・Next.jsでも使える |
まとめると:単純なキャッシュ → KVで十分。カウンター・レート制限・ランキング → Upstash一択です。
セットアップ:Upstashアカウント作成からCloudflare Workers連携まで
ステップ1:Upstashデータベースを作成
まず upstash.com でアカウントを作成します(Googleアカウントでログイン可)。
- コンソールにログイン → 「Redis」タブを選択
- 「Create Database」をクリック
- データベース名(例:
my-app-cache)を入力 - リージョンを選択(日本向けなら
ap-northeast-1= 東京) - 「Create」で完了
作成後、コンソールで「REST API」タブを開くと、接続に必要な UPSTASH_REDIS_REST_URL と UPSTASH_REDIS_REST_TOKEN が確認できます。
ステップ2:Cloudflare Workersプロジェクトに接続
既存のCloudflare Workersプロジェクト(またはHonoプロジェクト)にUpstashを追加します。
npm install @upstash/redis
次に、シークレットをWranglerで登録します(コードに直接書かないこと)。
wrangler secret put UPSTASH_REDIS_REST_URL
# プロンプトが出たら、UpstashコンソールのREST URLを貼り付ける
wrangler secret put UPSTASH_REDIS_REST_TOKEN
# 同様にトークンを貼り付ける
これで準備完了です。コード内では以下のように接続します。
import { Redis } from '@upstash/redis/cloudflare'
type Bindings = {
UPSTASH_REDIS_REST_URL: string
UPSTASH_REDIS_REST_TOKEN: string
}
// Cloudflare Workers環境では fromEnv() が便利
const redis = Redis.fromEnv(env)
// または明示的に指定
const redis = new Redis({
url: env.UPSTASH_REDIS_REST_URL,
token: env.UPSTASH_REDIS_REST_TOKEN,
})
実践1:APIレスポンスのキャッシュ実装
最も基本的なユースケースが「APIレスポンスのキャッシュ」です。外部APIへのリクエストを毎回発行するのは遅く、コストもかかります。Upstashを使えば、一度取得したデータをRedisに保存し、TTL(有効期限)内は高速で返せます。
import { Hono } from 'hono'
import { Redis } from '@upstash/redis/cloudflare'
type Bindings = {
UPSTASH_REDIS_REST_URL: string
UPSTASH_REDIS_REST_TOKEN: string
}
const app = new Hono<{ Bindings: Bindings }>()
app.get('/api/weather/:city', async (c) => {
const city = c.req.param('city')
const redis = Redis.fromEnv(c.env)
const cacheKey = `weather:${city}`
// キャッシュチェック
const cached = await redis.get<string>(cacheKey)
if (cached) {
return c.json({ data: JSON.parse(cached), cached: true })
}
// キャッシュミス → 外部APIから取得
const res = await fetch(
`https://api.weatherapi.com/v1/current.json?key=YOUR_KEY&q=${city}`
)
const data = await res.json()
// 30分間キャッシュ(ex = seconds)
await redis.set(cacheKey, JSON.stringify(data), { ex: 1800 })
return c.json({ data, cached: false })
})
export default app
ctx.waitUntil() を使うと、レスポンスを先にユーザーへ返してからバックグラウンドでキャッシュ更新できます。レイテンシをさらに改善したい場合に有効です。
// バックグラウンドでキャッシュ更新(レスポンスをブロックしない)
c.executionCtx.waitUntil(
redis.set(cacheKey, JSON.stringify(data), { ex: 1800 })
)
return c.json({ data, cached: false })
パイプラインで複数コマンドを一括実行
複数のキーを同時に取得・更新するとき、1コマンドずつ実行するとHTTPラウンドトリップが増えて遅くなります。パイプラインを使えば1回のリクエストにまとめられます。
const pipeline = redis.pipeline()
pipeline.get('config:theme')
pipeline.get('config:locale')
pipeline.incr('metrics:pageviews')
const [theme, locale, views] = await pipeline.exec()
// 1回のHTTPリクエストで3つのコマンドを実行!
実践2:APIレート制限を5分で実装する
個人開発でAPIを公開するとき、レート制限は必須のセキュリティ施策です。スパムや過剰なリクエストからサービスを守ります。Upstashは @upstash/ratelimit という専用ライブラリを提供しており、数行で実装できます。
npm install @upstash/ratelimit
import { Ratelimit } from '@upstash/ratelimit'
import { Redis } from '@upstash/redis/cloudflare'
import { Hono } from 'hono'
type Bindings = {
UPSTASH_REDIS_REST_URL: string
UPSTASH_REDIS_REST_TOKEN: string
}
const app = new Hono<{ Bindings: Bindings }>()
app.use('/api/*', async (c, next) => {
const redis = Redis.fromEnv(c.env)
// スライディングウィンドウ: 10秒に10リクエストまで
const ratelimit = new Ratelimit({
redis,
limiter: Ratelimit.slidingWindow(10, '10 s'),
analytics: true, // Upstashコンソールで使用状況を可視化
})
// IPアドレスをidentifierとして使用
const ip = c.req.header('CF-Connecting-IP') ?? 'anonymous'
const { success, limit, remaining, reset } = await ratelimit.limit(ip)
if (!success) {
return c.json(
{ error: 'Too many requests. Please try again later.' },
429,
{
'X-RateLimit-Limit': limit.toString(),
'X-RateLimit-Remaining': remaining.toString(),
'X-RateLimit-Reset': reset.toString(),
}
)
}
await next()
})
app.get('/api/data', (c) => {
return c.json({ message: 'Success!' })
})
export default app
アルゴリズムの選び方
レート制限アルゴリズムは用途によって使い分けます。
- slidingWindow(スライディングウィンドウ) — 最も滑らかな制限。一般的なAPIに最適
- fixedWindow(固定ウィンドウ) — シンプルで軽量。ウィンドウ切り替え時に一時的に制限が緩む特性あり
- tokenBucket(トークンバケット) — バースト許容あり。瞬間的なトラフィックスパイクを許容したい場合に有効
// ログインAPIは厳しく:1分に5回まで
const loginRatelimit = new Ratelimit({
redis,
limiter: Ratelimit.fixedWindow(5, '1 m'),
})
// 一般APIは緩く:1分に60回まで
const apiRatelimit = new Ratelimit({
redis,
limiter: Ratelimit.slidingWindow(60, '1 m'),
})
実践3:セッション管理とフィーチャーフラグ
セッションデータの保存
JWTを使ったステートレスな認証が主流ですが、セッション失効・強制ログアウトの仕組みを作りたいときはRedisが便利です。有効なセッションIDをRedisに保存しておき、リクエストのたびに存在チェックします。
// セッション作成
async function createSession(redis: Redis, userId: string): Promise<string> {
const sessionId = crypto.randomUUID()
await redis.set(
`session:${sessionId}`,
JSON.stringify({ userId, createdAt: Date.now() }),
{ ex: 60 * 60 * 24 * 7 } // 7日間
)
return sessionId
}
// セッション検証
async function validateSession(redis: Redis, sessionId: string) {
const data = await redis.get<string>(`session:${sessionId}`)
if (!data) return null
return JSON.parse(data)
}
// 強制ログアウト(管理者がセッションを無効化できる)
async function revokeSession(redis: Redis, sessionId: string) {
await redis.del(`session:${sessionId}`)
}
フィーチャーフラグでリリースを安全に
新機能を特定ユーザーにだけ公開する「フィーチャーフラグ」もRedisで簡単に実装できます。コードの再デプロイなしにフラグのON/OFFができるのが強みです。
// フラグの確認
async function isFeatureEnabled(redis: Redis, feature: string, userId: string) {
// グローバルフラグを確認
const globalFlag = await redis.get(`feature:${feature}`)
if (globalFlag === 'disabled') return false
if (globalFlag === 'enabled') return true
// ユーザー個別フラグを確認
const userFlag = await redis.get(`feature:${feature}:user:${userId}`)
return userFlag === 'enabled'
}
// 使用例
app.get('/api/new-ui', async (c) => {
const redis = Redis.fromEnv(c.env)
const
userId = c.get('userId')
if (!(await isFeatureEnabled(redis, 'new-dashboard', userId))) {
return c.json({ error: 'Feature not available' }, 403)
}
return c.json({ ui: 'new-dashboard-data' })
})
実践4:ランキング・スコアボードの実装
RedisのSorted Set(ソート済みセット)は、スコアボードやランキングを実装するのに特化したデータ構造です。スコアでソートされた集合を管理でき、「上位10件を取得」「特定ユーザーの順位を調べる」といった操作が高速に行えます。
// スコアを更新
await redis.zadd('leaderboard:global', {
score: 1500,
member: 'user:alice'
})
// 上位10位を取得(降順)
const top10 = await redis.zrange('leaderboard:global', 0, 9, {
rev: true,
withScores: true
})
// → [{ member: 'user:alice', score: 1500 }, ...]
// 特定ユーザーの順位を取得(0始まり)
const rank = await redis.zrevrank('leaderboard:global', 'user:alice')
console.log(`Alice is ranked #${(rank ?? 0) + 1}`)
// スコアを加算(既存スコアに追加)
await redis.zincrby('leaderboard:global', 100, 'user:alice')
このアーキテクチャは、ゲームスコア・記事いいね数ランキング・チャレンジ参加者順位など、様々なシーンで応用できます。
Upstash QStash:サーバーレスなジョブキュー
Upstashが提供するもう一つの強力なサービスが QStash です。Redisのキューとは別に、HTTPベースのジョブスケジューラーとして機能します。「30分後にメールを送る」「毎日9時にデータを集計する」といった非同期処理を、サーバーレス環境で実現できます。
npm install @upstash/qstash
import { Client } from '@upstash/qstash'
// ジョブをキューに投入
const client = new Client({ token: env.QSTASH_TOKEN })
// 5分後にメール送信エンドポイントを呼び出す
await client.publishJSON({
url: 'https://your-worker.workers.dev/api/send-email',
delay: '5m',
body: {
to: 'user@example.com',
subject: 'ご登録ありがとうございます',
}
})
// Cron形式でスケジュール(毎日9時)
await client.schedules.create({
destination: 'https://your-worker.workers.dev/api/daily-report',
cron: '0 9 * * *',
})
Cloudflare Workers単体ではCron Triggersがありますが、動的なスケジュール(ユーザーが設定した時間に実行)はQStashの方が柔軟です。
よくあるトラブルと解決方法
「connection refused」エラー
Cloudflare WorkersからUpstashへの接続に失敗する場合、@upstash/redis/cloudflare ではなく @upstash/redis を使っている可能性があります。Cloudflare Workers専用エントリーポイントをインポートしてください。
// ❌ Node.js用(Cloudflare Workersでは動かない場合がある)
import { Redis } from '@upstash/redis'
// ✅ Cloudflare Workers専用
import { Redis } from '@upstash/redis/cloudflare'
無料枠の上限に達した
Freeプランは1日10,000コマンドが上限です。redis.get() と redis.set() はそれぞれ1コマンドと数えます。頻繁にアクセスされるデータには長めのTTLを設定して、リクエスト数を抑えましょう。
// TTLを長めに設定してコマ
ンド数を削減
await redis.set('config', JSON.stringify(data), { ex: 3600 }) // 1時間
// 毎リクエストではなく、1時間に1回だけ外部APIを叩く
型安全なデータ取得
Upstash SDKはTypeScript対応ですが、get() の戻り値にジェネリクスを指定することで型安全に扱えます。
type UserData = { userId: string; name: string; plan: string }
// 型を指定して取得
const user = await redis.get<UserData>(`user:${id}`)
if (user) {
console.log(user.name) // 型推論が効く
}
他サービスとの組み合わせ:Upstashとの最強スタック
Upstashは単体で使うよりも、他のCloudflareサービスと組み合わせるとより強力になります。
- Hono.js + Cloudflare Workers — APIフレームワーク。UpstashをミドルウェアとしてHonoに組み込むのが定番構成
- Cloudflare D1 — SQLiteベースのDB。永続データはD1、一時キャッシュはUpstashと使い分けると効果的
- Cloudflare Workers + Micro SaaS — 個人開発SaaSのバックエンド全体像。Upstashはキャッシュ層として機能する
また、サービスが成長してきたら、PagePulse のような死活監視ツールでAPIの稼働状況を継続的にチェックしておくことをおすすめします。Redisへの接続が切れていないか、レスポンスタイムが正常かを自動で確認できます。
作ったサービスの稼働状況をユーザーに公開したい場合は、StatusCraft でステータスページを作成するのが手軽です。インシデント発生時の信頼性維持に役立ちます。
まとめ:Upstashで個人開発の品質を一段上げる
Upstashは「インフラ管理したくないけど、本格的なキャッシュやレート制限を実装したい」個人開発者にとって最適解の一つです。
- ✅ 無料枠で始められる(1日10,000コマンドまで)
- ✅ Cloudflare Workersと相性抜群(HTTP REST APIでTCP不要)
- ✅ レート制限が5分で実装できる(@upstash/ratelimit)
- ✅ Sorted Setでランキング機能も簡単
- ✅ QStashでサーバーレスジョブキューも対応
- ✅ TypeScript対応で型安全
「まずはレート制限だけ」から始めて、徐々にキャッシュ・セッション管理・ランキングと機能を追加していくのがおすすめです。Cloudflare WorkersにHono + D1 + Upstashを組み合わせたスタックは、2026年現在の個人開発バックエンドとして完成度が高く、月額コスト$0〜数ドルで本格的なWebサービスを運用できます。
ChromeでUpstashのドキュメントを読み進める際は、QuickSummary(AI要約Chrome拡張)を入れておくと、長文ドキュメントをAIで要約してくれて効率よく学習できます。