DEV Community

Alex Spinov
Alex Spinov

Posted on

Upstash Redis Has a Free Serverless Redis — Here's Why It's Perfect for Edge Functions

Traditional Redis needs a persistent connection. Upstash Redis works over HTTP — perfect for serverless and edge functions.

What is Upstash Redis?

Upstash provides serverless Redis with a REST API. No connection management, no connection pools, no TCP — just HTTP requests.

Free Tier

  • 10,000 commands/day
  • 256 MB max data size
  • Single region
  • REST API + Redis protocol

Quick Start

bun add @upstash/redis
Enter fullscreen mode Exit fullscreen mode
import { Redis } from '@upstash/redis';

const redis = new Redis({
  url: process.env.UPSTASH_REDIS_URL!,
  token: process.env.UPSTASH_REDIS_TOKEN!,
});

// Basic operations
await redis.set('user:123', { name: 'Alice', age: 30 });
const user = await redis.get('user:123');

// With TTL
await redis.set('session:abc', { userId: 123 }, { ex: 3600 }); // expires in 1 hour

// Increment
await redis.incr('page:views');
const views = await redis.get('page:views');
Enter fullscreen mode Exit fullscreen mode

Rate Limiting

import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';

const ratelimit = new Ratelimit({
  redis: Redis.fromEnv(),
  limiter: Ratelimit.slidingWindow(10, '10 s'), // 10 requests per 10 seconds
  analytics: true,
});

export async function middleware(req: Request) {
  const ip = req.headers.get('x-forwarded-for') || '127.0.0.1';
  const { success, limit, reset, remaining } = await ratelimit.limit(ip);

  if (!success) {
    return new Response('Too Many Requests', {
      status: 429,
      headers: {
        'X-RateLimit-Limit': limit.toString(),
        'X-RateLimit-Remaining': remaining.toString(),
        'X-RateLimit-Reset': reset.toString(),
      },
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

Caching

async function getCachedData(key: string, fetcher: () => Promise<any>, ttl = 300) {
  const cached = await redis.get(key);
  if (cached) return cached;

  const data = await fetcher();
  await redis.set(key, data, { ex: ttl });
  return data;
}

// Usage
const products = await getCachedData(
  'products:featured',
  () => db.products.findMany({ where: { featured: true } }),
  600 // cache for 10 minutes
);
Enter fullscreen mode Exit fullscreen mode

Session Storage

async function createSession(userId: string): Promise<string> {
  const sessionId = crypto.randomUUID();
  await redis.set(`session:${sessionId}`, { userId, createdAt: Date.now() }, { ex: 86400 });
  return sessionId;
}

async function getSession(sessionId: string) {
  return await redis.get(`session:${sessionId}`);
}

async function destroySession(sessionId: string) {
  await redis.del(`session:${sessionId}`);
}
Enter fullscreen mode Exit fullscreen mode

Works Everywhere (Edge-Compatible)

// Cloudflare Workers
export default {
  async fetch(request: Request): Promise<Response> {
    const redis = Redis.fromEnv();
    await redis.incr('worker:requests');
    return new Response('OK');
  },
};

// Vercel Edge Functions
import { Redis } from '@upstash/redis';

export const config = { runtime: 'edge' };

export default async function handler(req: Request) {
  const redis = Redis.fromEnv();
  const count = await redis.incr('edge:visits');
  return new Response(`Visit #${count}`);
}
Enter fullscreen mode Exit fullscreen mode

Upstash vs Traditional Redis

Feature Upstash Redis Self-hosted Redis ElastiCache
Protocol HTTP + Redis Redis (TCP) Redis (TCP)
Serverless Yes No No
Edge Compatible Yes No No
Connection Mgmt None needed Required Required
Pricing Per command Per server Per hour
Free Tier 10K cmd/day N/A N/A

Need Redis-backed scraping with caching? Check out my Apify actors — smart caching for efficient data extraction. For custom solutions, email spinov001@gmail.com.

Top comments (0)