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