ANTIGRAVITY LABJP
Articles/App Development
App Development/2026-03-25Intermediate

Antigravity × Upstash Redis: Build Fast, Secure APIs with Edge Caching and Rate Limiting

Learn how to implement edge caching and rate limiting on Cloudflare Workers using Antigravity and Upstash Redis, with step-by-step code examples.

antigravity435upstashrediscloudflare-workers8edge-cache2rate-limiting2serverless3

Why Edge Caching and Rate Limiting Matter

When you expose an API to the world, performance and security go hand in hand. In serverless environments, every request can trigger a cold start or a database round-trip, both of which eat into response times. Without rate limiting, a single bad actor can exhaust your resources and drive up costs.

Upstash Redis is a serverless Redis service accessible via HTTP REST API. Unlike traditional Redis that requires persistent TCP connections, Upstash works seamlessly in edge environments like Cloudflare Workers where TCP isn't available. That makes it the perfect companion for building fast, resilient APIs at the edge.

Prerequisites and Setup

Before diving in, you should be comfortable with the following:

What You'll Need

  • Antigravity (latest version)
  • Node.js 18 or later
  • Upstash account (free tier available at upstash.com)
  • Cloudflare account (the free Workers tier is enough)

Creating Your Upstash Redis Database

Head over to the Upstash Console and create a new Redis database. Pick a region close to your target users. Once it's created, grab these two values from the REST API section:

# From the Upstash Console REST API section
UPSTASH_REDIS_REST_URL=https://your-database.upstash.io
UPSTASH_REDIS_REST_TOKEN=AXxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Project Setup

Open Antigravity's terminal and scaffold a new Cloudflare Workers project using the Hono framework. Antigravity's agent can generate the boilerplate for you in seconds.

# Create the project
npm create hono@latest my-edge-api -- --template cloudflare-workers
cd my-edge-api
 
# Install the Upstash Redis client and rate limiter
npm install @upstash/redis @upstash/ratelimit

Then configure your environment variables in wrangler.toml:

# wrangler.toml
name = "my-edge-api"
compatibility_date = "2026-03-01"
 
[vars]
ENVIRONMENT = "production"
 
# Secrets should be set via wrangler CLI
# wrangler secret put UPSTASH_REDIS_REST_URL
# wrangler secret put UPSTASH_REDIS_REST_TOKEN

Implementing Edge Caching

Using Upstash Redis as a cache layer dramatically reduces database hits and speeds up responses. Here's a reusable caching middleware:

// src/cache.ts
import { Redis } from "@upstash/redis/cloudflare";
import type { Context, Next } from "hono";
 
// Initialize the Upstash Redis client
function getRedis(c: Context): Redis {
  return new Redis({
    url: c.env.UPSTASH_REDIS_REST_URL,
    token: c.env.UPSTASH_REDIS_REST_TOKEN,
  });
}
 
// Cache middleware
export function cacheMiddleware(ttlSeconds: number = 300) {
  return async (c: Context, next: Next) => {
    const redis = getRedis(c);
    const cacheKey = `cache:${c.req.url}`;
 
    // Try to get from cache
    const cached = await redis.get<string>(cacheKey);
    if (cached) {
      // Cache hit — return the cached response immediately
      return c.json(JSON.parse(cached), 200, {
        "X-Cache": "HIT",
        "X-Cache-TTL": String(ttlSeconds),
      });
    }
 
    // Cache miss — pass the request through to the origin
    await next();
 
    // Store successful responses in cache
    if (c.res.status === 200) {
      const body = await c.res.clone().text();
      await redis.set(cacheKey, body, { ex: ttlSeconds });
      c.res.headers.set("X-Cache", "MISS");
    }
  };
}
 
// Manually invalidate cache entries matching a pattern
export async function invalidateCache(
  redis: Redis,
  pattern: string
): Promise<number> {
  const keys = await redis.keys(`cache:${pattern}`);
  if (keys.length === 0) return 0;
 
  // Use pipeline to batch delete (minimizes network round-trips)
  const pipeline = redis.pipeline();
  keys.forEach((key) => pipeline.del(key));
  await pipeline.exec();
 
  return keys.length;
}

The X-Cache header makes it easy to tell whether a response came from cache or from the origin — super helpful for debugging.

Implementing Rate Limiting

With the @upstash/ratelimit package, you can set up sliding window rate limiting in just a few lines:

// src/ratelimit.ts
import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis/cloudflare";
import type { Context, Next } from "hono";
 
// Rate limiting middleware
export function rateLimitMiddleware(
  maxRequests: number = 60,
  windowSeconds: number = 60
) {
  return async (c: Context, next: Next) => {
    const redis = new Redis({
      url: c.env.UPSTASH_REDIS_REST_URL,
      token: c.env.UPSTASH_REDIS_REST_TOKEN,
    });
 
    const ratelimit = new Ratelimit({
      redis,
      // Sliding window: maxRequests allowed per windowSeconds
      limiter: Ratelimit.slidingWindow(maxRequests, `${windowSeconds} s`),
      analytics: true, // View analytics in the Upstash Console
    });
 
    // Identify the client by API key or IP address
    const identifier =
      c.req.header("X-API-Key") ||
      c.req.header("CF-Connecting-IP") ||
      "anonymous";
 
    const { success, limit, remaining, reset } =
      await ratelimit.limit(identifier);
 
    // Always include rate limit headers
    c.res.headers.set("X-RateLimit-Limit", String(limit));
    c.res.headers.set("X-RateLimit-Remaining", String(remaining));
    c.res.headers.set("X-RateLimit-Reset", String(reset));
 
    if (!success) {
      // Rate limit exceeded — return 429 Too Many Requests
      return c.json(
        {
          error: "Too Many Requests",
          message: `Rate limit exceeded. Try again after ${new Date(reset).toISOString()}`,
          retryAfter: Math.ceil((reset - Date.now()) / 1000),
        },
        429
      );
    }
 
    await next();
  };
}

The sliding window algorithm prevents bursts at window boundaries, giving you fairer rate limiting compared to fixed windows.

Wiring It All Together

Now let's integrate both middlewares into a Hono application:

// src/index.ts
import { Hono } from "hono";
import { cors } from "hono/cors";
import { cacheMiddleware, invalidateCache } from "./cache";
import { rateLimitMiddleware } from "./ratelimit";
import { Redis } from "@upstash/redis/cloudflare";
 
type Bindings = {
  UPSTASH_REDIS_REST_URL: string;
  UPSTASH_REDIS_REST_TOKEN: string;
};
 
const app = new Hono<{ Bindings: Bindings }>();
 
// Global middleware
app.use("*", cors());
app.use("/api/*", rateLimitMiddleware(100, 60)); // 100 requests per 60 seconds
 
// Cached endpoint (5-minute TTL)
app.get("/api/articles", cacheMiddleware(300), async (c) => {
  // Your actual data fetching logic (DB, external API, etc.)
  const articles = await fetchArticlesFromDB();
  return c.json({ articles, timestamp: new Date().toISOString() });
});
 
// Uncached endpoint (for real-time data)
app.get("/api/stats", async (c) => {
  const redis = new Redis({
    url: c.env.UPSTASH_REDIS_REST_URL,
    token: c.env.UPSTASH_REDIS_REST_TOKEN,
  });
 
  // Use Redis as a real-time counter
  const pageViews = await redis.incr("stats:pageviews");
  return c.json({ pageViews });
});
 
// Cache purge endpoint (admin only)
app.post("/api/cache/purge", async (c) => {
  const redis = new Redis({
    url: c.env.UPSTASH_REDIS_REST_URL,
    token: c.env.UPSTASH_REDIS_REST_TOKEN,
  });
 
  const deleted = await invalidateCache(redis, "*articles*");
  return c.json({ purged: deleted });
});
 
// Placeholder data fetching (replace with real DB calls in production)
async function fetchArticlesFromDB() {
  return [
    { id: 1, title: "Getting Started with Edge Computing" },
    { id: 2, title: "Redis Patterns for Serverless" },
  ];
}
 
export default app;

Getting the Most Out of Antigravity's Agent

Antigravity's agent mode excels at generating and refactoring infrastructure code like this. Here are some prompt ideas to try:

# Ask for cache strategy advice
"Suggest optimal cache TTLs for these endpoints:
  - /api/articles → updates a few times per day
  - /api/user/profile → updates on user action
  - /api/stats → needs real-time accuracy"

# Design tiered rate limits
"Design rate limiting for these tiers:
  - Free users: 60 req/min
  - Pro users: 300 req/min
  - Identify by API key"

The agent also handles wrangler.toml config and TypeScript type definitions in one go, so you won't miss any configuration steps. For more on building serverless APIs with Antigravity, see Antigravity × Hono: Building Serverless APIs on Workers.

Deploying and Testing

Deploy with Wrangler and verify everything works:

# Local development
npx wrangler dev
 
# Set secrets
npx wrangler secret put UPSTASH_REDIS_REST_URL
npx wrangler secret put UPSTASH_REDIS_REST_TOKEN
 
# Production deploy
npx wrangler deploy

After deploying, test the cache and rate limiting behavior:

# First request: cache MISS
curl -i https://my-edge-api.your-subdomain.workers.dev/api/articles
# → X-Cache: MISS
 
# Second request: cache HIT (much faster)
curl -i https://my-edge-api.your-subdomain.workers.dev/api/articles
# → X-Cache: HIT
 
# Rate limit test (fire 105 requests)
for i in $(seq 1 105); do
  echo "Request $i: $(curl -s -o /dev/null -w '%{http_code}' https://my-edge-api.your-subdomain.workers.dev/api/articles)"
done
# → Requests 101+ return 429

Common Errors and How to Fix Them

Upstash Connection Errors

If UPSTASH_REDIS_REST_URL or UPSTASH_REDIS_REST_TOKEN aren't set correctly, you'll see something like:

Error: Unauthorized - Invalid token

Run wrangler secret list to confirm your secrets are registered. For local development, put your environment variables in a .dev.vars file.

Stale Cache Data

If your cache TTL is too long, users will see outdated data after updates. Either hit the /api/cache/purge endpoint when content changes, or adopt a write-through cache pattern that updates the cache on every write.

Rate Limits Too Strict or Too Loose

Enable analytics: true in your Ratelimit config and check the Upstash Console dashboard to see actual traffic patterns. Adjust maxRequests and windowSeconds based on real data rather than guesswork.

Wrapping Up

By combining Antigravity with Upstash Redis, you can build fast, secure APIs on Cloudflare Workers with minimal effort.

Here's what we covered:

  1. Upstash Redis uses HTTP REST, making it compatible with edge environments where TCP connections aren't available
  2. Cache middleware reduces database load and dramatically improves response times
  3. Rate limiting middleware with sliding windows provides fair, burst-resistant access control
  4. Antigravity's agent accelerates infrastructure code generation and keeps configurations consistent

As a next step, check out Designing Secure API Backend Patterns with Antigravity to add authentication and authorization to your edge API.

Share

Thank You for Reading

Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

App Dev2026-06-14
When the Edge Cache Pinned Next.js Error Pages: A cache-worker Guard Design
Users reported intermittent 'failed to load' errors I could never reproduce. The cause: SSR exceptions shipped as HTTP 200 and pinned by the edge cache. Here is how I narrowed it down with an Antigravity agent and added a cache-worker guard to stop it.
App Dev2026-04-28
Build a Complete SaaS in Antigravity — Stripe Billing, Auth, and Deployment Without Leaving Your IDE
Build an entire SaaS product within Antigravity IDE — from Supabase auth to Stripe billing to Cloudflare Workers deployment, without switching tools.
App Dev2026-04-27
Building Webhook Endpoints in Antigravity That Never Drop Events
A practical guide to designing, implementing, and testing webhook endpoints for Stripe and GitHub using Antigravity, focused on the three properties that prevent dropped events: signature verification, idempotency, and fast response.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →