Two everyday Redis jobs beyond caching — a fixed-window rate limiter built from INCR and EXPIRE, and a fast server-side session store. Both are a handful of lines.
Rate limiting protects an endpoint from abuse by capping requests per client per time window. The trick uses two commands: incr the client's counter, and on the first hit of a window set an expire so the window resets itself. If the count passes your limit, reject with 429. Key it by user id or IP plus the route.
// Allow 10 requests per minute per client.
async function rateLimit(clientId: string, limit = 10, windowSec = 60) {
const key = 'rl:' + clientId
const count = await redis.incr(key) // 1 on the first request
if (count === 1) {
await redis.expire(key, windowSec) // start the window's countdown
}
return { allowed: count <= limit, remaining: Math.max(0, limit - count) }
}
// In a route handler:
const { allowed } = await rateLimit(ip)
if (!allowed) return Response.json({ error: 'Too many requests' }, { status: 429 })Sessions are a natural Redis fit: small, short-lived, read on every request, and they expire on their own. Store the session data as JSON under a random id with a TTL, hand the id to the client in a cookie, and load it back by id. Refreshing the TTL on each request keeps active users logged in and lets idle ones expire.
import { randomUUID } from 'crypto'
async function createSession(userId: string) {
const id = randomUUID()
// SETEX = set with expiry (seconds). 1 day here.
await redis.setex('session:' + id, 86400, JSON.stringify({ userId }))
return id // put this in an HttpOnly cookie
}
async function getSession(id: string) {
const raw = await redis.get('session:' + id)
if (!raw) return null
await redis.expire('session:' + id, 86400) // sliding refresh
return JSON.parse(raw) // { userId }
}