home / skills / openclaw / skills / vercel-to-cloudflare

vercel-to-cloudflare skill

/skills/jiafar/vercel-to-cloudflare

This skill helps you migrate a Next.js app from Vercel to Cloudflare Workers, configure Hyperdrive, and fix Supabase connectivity.

npx playbooks add skill openclaw/skills --skill vercel-to-cloudflare

Review the files below or copy the command above to add this skill to your agents.

Files (5)
SKILL.md
3.9 KB
---
name: vercel-to-cloudflare
description: Migrate Next.js projects from Vercel to Cloudflare Workers with Supabase/Hyperdrive support. Use when user wants to move a Next.js app off Vercel to reduce costs, deploy to Cloudflare Workers, configure Hyperdrive connection pooling, or fix Supabase connectivity issues on Cloudflare. Triggers on phrases like "migrate to Cloudflare", "Vercel too expensive", "deploy Next.js on Cloudflare Worker", "Cloudflare Hyperdrive setup", "Supabase on Cloudflare", "从Vercel迁移到Cloudflare", "Vercel太贵了", "部署到Cloudflare Worker".
---

# Vercel to Cloudflare Worker Migration

Migrate a Next.js + Supabase project from Vercel to Cloudflare Workers with Hyperdrive connection pooling.

## Quick Start

1. Run the analysis script to scan the project:
   ```bash
   python3 scripts/analyze_project.py <project-path>
   ```
2. Review the migration report
3. Run the migration script:
   ```bash
   python3 scripts/migrate.py <project-path>
   ```
4. Configure Hyperdrive: see [references/hyperdrive-setup.md](references/hyperdrive-setup.md)

## Core Migration Steps

### 1. Install @opennextjs/cloudflare adapter

```bash
npm install @opennextjs/cloudflare
```

Update `next.config.js` or `next.config.ts` if needed.

### 2. Rewrite environment variable access

All `process.env.XXX` for Cloudflare bindings (Hyperdrive, KV, D1, etc.) must use `getCloudflareContext()`:

```typescript
// BEFORE (Vercel/Node.js)
const url = process.env.DATABASE_URL;

// AFTER (Cloudflare Worker)
import { getCloudflareContext } from '@opennextjs/cloudflare';

function getConnectionInfo() {
  const env = getCloudflareContext().env;
  const hyperdrive = env.HYPERDRIVE as { connectionString?: string } | undefined;
  if (hyperdrive?.connectionString) {
    return { connectionString: hyperdrive.connectionString, source: 'hyperdrive' };
  }
  // Fallback for local dev
  const local = env.CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_HYPERDRIVE;
  if (local) {
    return { connectionString: local, source: 'hyperdrive-local' };
  }
  throw new Error('HYPERDRIVE is not configured');
}
```

### 3. Refactor global DB singleton to per-request pattern

```typescript
// BEFORE: Global singleton
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
const client = postgres(process.env.DATABASE_URL!);
export const db = drizzle(client);

// AFTER: Per-request with React cache
import { cache } from 'react';

export const getDb = cache(() => {
  const { connectionString, source } = getConnectionInfo();
  return createDatabase({
    connectionString,
    enableSSL: source === 'hyperdrive' ? false : 'require',
  });
});
```

Then replace all `import { db }` with `import { getDb }` and add `const db = getDb()` at the start of each function.

### 4. Configure wrangler.toml

```toml
name = "my-app"
main = ".open-next/worker.js"
compatibility_date = "2024-09-23"
compatibility_flags = ["nodejs_compat"]

[[hyperdrive]]
binding = "HYPERDRIVE"
id = "<your-hyperdrive-id>"
```

## Critical Pitfalls

1. **Hyperdrive must connect to Supabase Direct Connection** (port 5432), NOT the Pooler (port 6543). Hyperdrive IS a connection pooler — connecting pooler-to-pooler causes errors.

2. **SSL must be disabled for Hyperdrive connections** — Worker ↔ Hyperdrive is internal network. Only enable SSL for direct database connections (local dev, build stage).

3. **Cannot initialize DB at module top level** — `getCloudflareContext()` only works during request handling, not at module load time.

4. **Supabase Free Tier direct connection is IPv6 only** — local dev may fail if your network doesn't support IPv6. Use the Pooler URL (port 6543) for local development.

## Detailed References

- **Hyperdrive setup & Supabase config**: Read [references/hyperdrive-setup.md](references/hyperdrive-setup.md)
- **Environment variable patterns**: Read [references/env-patterns.md](references/env-patterns.md)

Overview

This skill migrates Next.js projects from Vercel to Cloudflare Workers and configures Supabase/Hyperdrive connection pooling. It automates analysis, code rewrites, and deployment configuration to reduce hosting costs and ensure correct database connectivity. It focuses on safe per-request DB initialization and Cloudflare-specific environment access patterns. The result is a Cloudflare Worker–ready Next.js app with Hyperdrive-compatible database connections.

How this skill works

The skill scans a Next.js project to produce a migration report, then applies automated code changes and generates deployment settings. It replaces process.env access with Cloudflare context access, converts global DB singletons into per-request factories, and updates configuration files such as next.config and wrangler.toml. It also provides guidance for Hyperdrive and Supabase pitfalls and creates a Hyperdrive-friendly connection pattern.

When to use it

  • You want to move a Next.js app off Vercel to cut hosting costs
  • Deploying Next.js on Cloudflare Workers using @opennextjs/cloudflare
  • Setting up Hyperdrive connection pooling for Supabase on Cloudflare
  • Fixing Supabase connectivity issues when running on Cloudflare Workers
  • Preparing a project for Cloudflare Worker compatibility and node-compat mode

Best practices

  • Use getCloudflareContext() for all Cloudflare bindings instead of process.env
  • Refactor any module-level DB initialization to a per-request factory using caching
  • Configure Hyperdrive to connect to Supabase direct connection (port 5432) — not to the Pooler
  • Disable SSL for Hyperdrive connections between Worker and Hyperdrive; enable SSL only for direct external connections
  • Test local development with the Pooler URL (port 6543) if your network lacks IPv6 support

Example use cases

  • Run the analysis script to identify Vercel-specific patterns and get a migration plan
  • Automate code changes that swap process.env usage for Cloudflare context bindings
  • Refactor drizzle/postgres singletons into cached per-request factories for Workers
  • Generate a wrangler.toml with Hyperdrive binding and nodejs_compat compatibility flags
  • Resolve runtime errors by switching Hyperdrive to Supabase direct connection and adjusting SSL settings

FAQ

Will this break local development workflows?

The migration preserves local dev fallbacks. You must keep explicit local connection strings for development and use the Pooler URL if your network lacks IPv6.

Why can’t I initialize the DB at module top level?

getCloudflareContext() is only available during request handling in Workers. Initializing at module load time prevents access to Cloudflare bindings and causes runtime errors.