Upstash Redis
In short: A “serverless Redis” provider — an in-memory database for fast, short-lived data (e.g. sessions, rate-limiting counters), accessible via a REST API instead of a classic Redis connection.
In more detail: Access runs via a REST URL + token (UPSTASH_REDIS_REST_URL/_TOKEN), which can be viewed permanently in the dashboard. If the connection is missing, many applications fall into a fallback mode, which often shows up as sudden slowness.
Our context: Used at Emzett for, among other things, global rate limiting — when the Redis connection was missing after the move, this led to noticeably slow pages (every request tried unsuccessfully to connect).
In Depth
Classic Redis usually runs as a permanently open TCP connection that is held in a long-running server process. Serverless environments (e.g. Vercel Functions), however, constantly start and stop function instances — a permanent TCP connection doesn’t fit this model, and every new instance would first have to establish an expensive connection again. Upstash solves this by accepting Redis commands over ordinary HTTP requests (GET/POST with the command in the body instead of a dedicated binary protocol) — which makes it fit exactly into the “short-lived function calls” model, without connection-pooling problems.
Typical uses for an in-memory database like this are data that has to be available quickly but doesn’t need to end up permanently in the main database (PostgreSQL/Neon): session state, counters for rate limiting, short-lived caches, or feature flag values (see Feature flags). An important difference from a normal database: Redis values can be given a TTL (time to live) — they disappear automatically after a deadline, without a cleanup job of your own.
await redis.set(`ratelimit:${ip}`, count, { ex: 60 }) // expires automatically after 60sMore than just key-value
Beyond simple string values, Redis (and therefore Upstash) offers specialised data structures that are much more efficient for certain tasks than rebuilding them in a classic relational database: sorted sets (ZADD/ZRANGE) are ideal for leaderboards, for example (values automatically sorted by score), lists (LPUSH/RPOP) for simple queues, and hashes for structured objects under a single key, without needing a separate Redis key for every field.
Sliding-window vs. fixed-window rate limiting
For rate limiting (as in the example above) there are two common strategies: fixed window counts requests within fixed time windows (e.g. “0-60s”, “60-120s”) — simple to implement, but it has a weakness at the window boundaries (a user could use the full limit both just before AND just after a boundary, getting twice as many requests through in a short time as intended). Sliding window instead always looks at the last N seconds relative to the current moment, independent of fixed boundaries — more accurate, but somewhat more complex to implement. Ready-made libraries such as @upstash/ratelimit offer both strategies directly, without you having to rebuild them yourself.
See also: Rate Limiting, TTL, Neon