Skip to main content
This guide shows how to use Upstash Redis with Cloudflare Workers. The HTTP-based SDK is specifically designed for edge computing environments where traditional TCP connections aren’t available.

Why Upstash Redis for Cloudflare Workers?

  • Edge-native: HTTP-based connection perfect for Workers
  • Global low latency: Access Redis from 300+ cities worldwide
  • No cold starts: Instant execution at the edge
  • Automatic scaling: Handles traffic spikes seamlessly

Prerequisites

1

Create Cloudflare Account

Sign up for a Cloudflare account
2

Install Wrangler CLI

Install the Cloudflare Workers CLI:
npm install -g wrangler
3

Create Upstash Redis Database

Create a Redis database on Upstash Console

Quick Start

Project Setup

1

Clone Example or Create New Project

git clone https://github.com/upstash/upstash-redis.git
cd upstash-redis/examples/cloudflare-workers
Or create a new project:
mkdir my-worker
cd my-worker
npm init -y
2

Install Dependencies

npm install @upstash/redis
npm install -D wrangler

Example: Counter Worker

This example implements a simple counter that increments on each request.
import { Redis } from "@upstash/redis/cloudflare";

export default {
  async fetch(_request, env) {
    const redis = Redis.fromEnv(env);

    const count = await redis.incr("cloudflare-workers-count");

    return new Response(JSON.stringify({ count }));
  },
};
Notice the import path: @upstash/redis/cloudflare - This is important for Cloudflare Workers compatibility.

Configuration

Add Environment Variables

You can configure environment variables in two ways:
wrangler.toml
name = "my-worker"
main = "index.js"
compatibility_date = "2024-01-01"

[vars]
UPSTASH_REDIS_REST_URL = "https://your-endpoint.upstash.io"
UPSTASH_REDIS_REST_TOKEN = "your-token"
1

Go to Workers Dashboard

2

Select Your Worker

Click on your Worker or create a new one
3

Add Environment Variables

Go to Settings > Variables and add:
  • UPSTASH_REDIS_REST_URL
  • UPSTASH_REDIS_REST_TOKEN

Option 3: Wrangler CLI Secrets (Most Secure)

wrangler secret put UPSTASH_REDIS_REST_URL
wrangler secret put UPSTASH_REDIS_REST_TOKEN

Development

Run Locally

1

Start Development Server

npm run start
# or
wrangler dev
2

Test Your Worker

Open your browser at localhost:8787

Deploy to Cloudflare

npm run publish
# or
wrangler deploy
Your Worker will be deployed to Cloudflare’s global network and accessible via a *.workers.dev URL.

Advanced Examples

REST API with Routing

import { Redis } from "@upstash/redis/cloudflare";

export interface Env {
  UPSTASH_REDIS_REST_URL: string;
  UPSTASH_REDIS_REST_TOKEN: string;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const redis = Redis.fromEnv(env);
    const url = new URL(request.url);
    const path = url.pathname;

    // GET /counter - Get current count
    if (path === "/counter" && request.method === "GET") {
      const count = await redis.get<number>("counter") || 0;
      return new Response(JSON.stringify({ count }), {
        headers: { "Content-Type": "application/json" },
      });
    }

    // POST /counter/increment - Increment counter
    if (path === "/counter/increment" && request.method === "POST") {
      const count = await redis.incr("counter");
      return new Response(JSON.stringify({ count }), {
        headers: { "Content-Type": "application/json" },
      });
    }

    // POST /counter/reset - Reset counter
    if (path === "/counter/reset" && request.method === "POST") {
      await redis.set("counter", 0);
      return new Response(JSON.stringify({ count: 0 }), {
        headers: { "Content-Type": "application/json" },
      });
    }

    return new Response("Not found", { status: 404 });
  },
};

Edge Caching

import { Redis } from "@upstash/redis/cloudflare";

export interface Env {
  UPSTASH_REDIS_REST_URL: string;
  UPSTASH_REDIS_REST_TOKEN: string;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const redis = Redis.fromEnv(env);
    const url = new URL(request.url);
    const cacheKey = `cache:${url.pathname}`;

    // Try cache first
    const cached = await redis.get(cacheKey);
    if (cached) {
      return new Response(JSON.stringify(cached), {
        headers: {
          "Content-Type": "application/json",
          "X-Cache": "HIT",
        },
      });
    }

    // Simulate fetching data
    const data = {
      message: "Hello from the edge!",
      timestamp: new Date().toISOString(),
      location: request.cf?.city || "Unknown",
    };

    // Cache for 1 minute
    await redis.set(cacheKey, data, { ex: 60 });

    return new Response(JSON.stringify(data), {
      headers: {
        "Content-Type": "application/json",
        "X-Cache": "MISS",
      },
    });
  },
};

Rate Limiting at the Edge

import { Redis } from "@upstash/redis/cloudflare";

export interface Env {
  UPSTASH_REDIS_REST_URL: string;
  UPSTASH_REDIS_REST_TOKEN: string;
}

const RATE_LIMIT = 10; // requests per minute
const WINDOW = 60; // seconds

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const redis = Redis.fromEnv(env);
    
    // Use CF-Connecting-IP header for client IP
    const ip = request.headers.get("CF-Connecting-IP") || "unknown";
    const key = `rate_limit:${ip}`;

    // Get current count
    const current = await redis.incr(key);

    // Set expiration on first request
    if (current === 1) {
      await redis.expire(key, WINDOW);
    }

    // Check if limit exceeded
    if (current > RATE_LIMIT) {
      const ttl = await redis.ttl(key);
      return new Response(
        JSON.stringify({
          error: "Rate limit exceeded",
          retryAfter: ttl,
        }),
        {
          status: 429,
          headers: {
            "Content-Type": "application/json",
            "Retry-After": ttl.toString(),
          },
        }
      );
    }

    // Process request
    return new Response(
      JSON.stringify({
        message: "Success",
        remaining: RATE_LIMIT - current,
      }),
      {
        headers: {
          "Content-Type": "application/json",
          "X-RateLimit-Limit": RATE_LIMIT.toString(),
          "X-RateLimit-Remaining": (RATE_LIMIT - current).toString(),
        },
      }
    );
  },
};

Session Management

import { Redis } from "@upstash/redis/cloudflare";

interface Session {
  id: string;
  userId?: string;
  createdAt: number;
  lastActivity: number;
  data: Record<string, any>;
}

export interface Env {
  UPSTASH_REDIS_REST_URL: string;
  UPSTASH_REDIS_REST_TOKEN: string;
}

function generateSessionId(): string {
  return crypto.randomUUID();
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const redis = Redis.fromEnv(env);
    
    // Get or create session
    let sessionId = request.headers.get("X-Session-ID");
    let session: Session | null = null;

    if (sessionId) {
      session = await redis.get<Session>(`session:${sessionId}`);
    }

    if (!session) {
      sessionId = generateSessionId();
      session = {
        id: sessionId,
        createdAt: Date.now(),
        lastActivity: Date.now(),
        data: {},
      };
    }

    // Update session activity
    session.lastActivity = Date.now();

    // Store session with 1 hour expiration
    await redis.set(`session:${sessionId}`, session, { ex: 3600 });

    return new Response(
      JSON.stringify({
        sessionId,
        session,
      }),
      {
        headers: {
          "Content-Type": "application/json",
          "X-Session-ID": sessionId,
        },
      }
    );
  },
};

TypeScript Support

For full TypeScript support, use the TypeScript template:
wrangler init my-worker --type typescript
Define your environment interface:
export interface Env {
  UPSTASH_REDIS_REST_URL: string;
  UPSTASH_REDIS_REST_TOKEN: string;
  // Add other environment variables here
}

Best Practices

Initialize Redis Client Inside Handler

Unlike traditional environments, Workers don’t maintain state between requests. Initialize the client in each request:
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const redis = Redis.fromEnv(env); // Initialize per request
    // Use redis...
  },
};

Error Handling

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    try {
      const redis = Redis.fromEnv(env);
      const data = await redis.get("key");
      
      return new Response(JSON.stringify({ data }), {
        headers: { "Content-Type": "application/json" },
      });
    } catch (error) {
      console.error("Redis error:", error);
      return new Response(
        JSON.stringify({ error: "Internal server error" }),
        {
          status: 500,
          headers: { "Content-Type": "application/json" },
        }
      );
    }
  },
};

Use Edge Location Data

Cloudflare provides request context with location data:
const location = {
  country: request.cf?.country,
  city: request.cf?.city,
  timezone: request.cf?.timezone,
  latitude: request.cf?.latitude,
  longitude: request.cf?.longitude,
};

// Store location-specific data
await redis.set(`visitor:${ip}`, location);

Testing

Local Testing with Wrangler

wrangler dev
Test endpoints:
curl http://localhost:8787/counter
curl -X POST http://localhost:8787/counter/increment

Unit Testing

For unit testing Workers, see Cloudflare Workers testing documentation.

Monitoring

View Logs

wrangler tail
Or view logs in the Cloudflare Dashboard.

Add Custom Logging

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    console.log("Request:", request.url);
    
    const start = Date.now();
    const redis = Redis.fromEnv(env);
    const result = await redis.get("key");
    const duration = Date.now() - start;
    
    console.log(`Redis GET completed in ${duration}ms`);
    
    return new Response(JSON.stringify(result));
  },
};

Troubleshooting

Make sure you’re using the correct import path:
import { Redis } from "@upstash/redis/cloudflare";
Not:
import { Redis } from "@upstash/redis"; // Wrong for Workers!
Ensure variables are:
  1. Set in wrangler.toml [vars] section, OR
  2. Set in Cloudflare Dashboard, OR
  3. Set as secrets via wrangler secret put
Access them via the env parameter:
Redis.fromEnv(env) // Pass the env object
Check your wrangler.toml configuration:
  • name must be unique
  • main must point to your entry file
  • compatibility_date should be recent

Next Steps

AWS Lambda

Deploy Redis with AWS Lambda

Next.js

Integrate Redis with Next.js

Basic Usage

Learn more Redis operations

Cloudflare Docs

Cloudflare Workers documentation

Build docs developers (and LLMs) love