Redis:始める
2026年3月11日Redisはメモリ(RAM)上にデータを保存・管理する専用のデータベース
Postgresはディスク(SSD/HDD)にデータを保存する
メモリにデータを保存するので読み書きが高速
Local:TCP
Upstashで触るサーバーレス用のHTTP Redisと書き方はほとんど変わらない。以下はDockerで触るためのセットアップ
docker-compose.yml:
services:
redis:
image: redis:8.2-alpine
container_name: local-redis
ports:
- "6379:6379"
command: ["redis-server", "--appendonly", "yes"]
volumes:
- redis-data:/data
restart: unless-stopped
volumes:
redis-data:redis.ts:
import Redis from "ioredis";
export const redis = new Redis({
host: "127.0.0.1",
port: 6379,
});データのセット・取得:
const res = await redis.set("key2", "hello2", "EX", 10);
const value = await redis.get("key");
console.log(res , value); EXはTTL(有効期限)の設定
resには"OK"という文字のみが返る
Upstash:HTTP
Vercelから統合できる。環境変数名はVercelとUpstashで異なるが、値は同じ
KV_REST_API_TOKEN="*****************"
KV_REST_API_URL="***************"
UPSTASH_REDIS_REST_URL=
UPSTASH_REDIS_REST_TOKEN=使い方
redis.ts:
import { Redis } from "@upstash/redis";
export const redis = new Redis({
url: process.env.KV_REST_API_URL,
token: process.env.KV_REST_API_TOKEN,
});api/redis/test/route.ts:
import { redis } from "@/lib/resis";
export async function GET() {
const res = await redis.set("hello", "world", {});
return Response.json(
{ message: "send redis" },
{
status: 200,
},
);
}option
set(key , value , {})の {} に設定するオプション
await redis.set("hello", "world2", {
ex: 30,
});RateLimit
route.ts:
import { redis } from "@/lib/redis";
import { NextRequest, NextResponse } from "next/server";
const MAX_REQUESTS = 5; // 60秒で5リクエスト
const WINDOW_SEC = 60;
export async function GET(req: NextRequest) {
const ip = req.headers.get("x-forwarded-for")?.split(",")[0] || "unknown";
const key = `ratelimit:${ip}`;
const requests = await redis.incr(key);
if (requests === 1) {
await redis.expire(key, WINDOW_SEC);
}
if (requests > MAX_REQUESTS) {
return NextResponse.json({ message: "Too Many Requests" }, { status: 429 });
}
// 成功時の処理
await redis.set("hello", "world", {
ex: 20,
});
const value = await redis.get("hello");
return NextResponse.json({ message: "send redis", value });
}redis.incrはkeyの値を1増やすredis.expireはキーに有効期限を設定request===1のとき、つまり初回リクエスト時に有効期限を設定しているので60秒にはキーが削除される
@upstash/ratelimit
zustandと合わせる
外部APIへの問い合わせを減らす
クライアント側はzustandを使ってリロードが行われるまでデータをキャッシュ、サーバー側はredisで設定した有効期限までは外部APIではなくredisに問い合わせる
以下は天気APIを使った例
route.ts:
import { redis } from "@/lib/redis/redis";
import { NextRequest, NextResponse } from "next/server";
const API_KEY = process.env.OPENWEATHER_API_KEY!;
export async function GET(req: NextRequest) {
const city = req.nextUrl.searchParams.get("city");
if (!city) {
return NextResponse.json({ error: "Missing city" }, { status: 400 });
}
const cacheKey = `city:${city.toLowerCase()}`;
try {
const cached = await redis.get(cacheKey);
if (cached) {
return NextResponse.json(cached);
}
const res = await fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${API_KEY}&units=metric`
);
if (!res.ok) {
return NextResponse.json({ error: "Failed to fetch weather" }, { status: res.status });
}
const data = await res.json();
await redis.set(cacheKey, JSON.stringify(data), { ex: 60 });
return NextResponse.json(data);
} catch (err) {
console.error(err);
return NextResponse.json({ error: "Internal server error" }, { status: 500 });
}
}cacheKeyを宣言
redisからcacheKeyキーを持つデータがあるか確認し、あればreturn
cacheKeyが無い場合は外部APIからデータをfetchし、redisに保存した後にretun
weather-store.ts:
import { create } from "zustand";
export interface WeatherData {
name: string;
main: { temp: number; humidity: number };
weather: { description: string; icon: string }[];
}
type WeatherStore = {
weather: Record<string, WeatherData | null>;
fetchWeather: (city: string) => Promise<WeatherData | null>;
};
export const useWeatherStore = create<WeatherStore>((set, get) => ({
weather: {},
fetchWeather: async (city: string) => {
const cached = get().weather[city.toLowerCase()];
if (cached) return cached;
try {
const res = await fetch(`/api/weather?city=${city}`, { cache: "no-store" });
if (!res.ok) return null;
const data: WeatherData = await res.json();
set((state) => ({
weather: { ...state.weather, [city.toLowerCase()]: data },
}));
return data;
} catch (err) {
console.error(err);
return null;
}
},
}));database
/cache
redis