Bỏ qua

OpenNext Adapter cho Cloudflare

OpenNext là gì?

OpenNext là adapter cho phép deploy Next.js lên các platforms khác ngoài Vercel.

┌─────────────────────────────────────────────────────────────┐
│                      Next.js Application                     │
└───────────────────────────┬─────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│                     OpenNext Adapter                         │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │   Server    │  │   Image     │  │   Cache     │         │
│  │   Handler   │  │   Handler   │  │   Handler   │         │
│  └─────────────┘  └─────────────┘  └─────────────┘         │
└───────────────────────────┬─────────────────────────────────┘
          ┌─────────────────┼─────────────────┐
          ▼                 ▼                 ▼
    ┌──────────┐      ┌──────────┐      ┌──────────┐
    │   AWS    │      │Cloudflare│      │ Netlify  │
    │  Lambda  │      │ Workers  │      │Functions │
    └──────────┘      └──────────┘      └──────────┘

Cài đặt @opennextjs/cloudflare

Step 1: Install package

npm install @opennextjs/cloudflare

Step 2: Tạo file cấu hình

// open-next.config.ts
import type { OpenNextConfig } from '@opennextjs/cloudflare';

const config: OpenNextConfig = {
  default: {
    override: {
      wrapper: 'cloudflare-node',
      converter: 'edge',
      incrementalCache: 'dummy',
      tagCache: 'dummy',
      queue: 'dummy',
    },
  },

  middleware: {
    external: true,
    override: {
      wrapper: 'cloudflare-edge',
      converter: 'edge',
      proxyExternalRequest: 'fetch',
    },
  },
};

export default config;

Cấu hình Wrangler

wrangler.toml

#:schema node_modules/wrangler/config-schema.json
name = "my-nextjs-app"
main = ".open-next/worker.js"
compatibility_date = "2024-09-23"
compatibility_flags = ["nodejs_compat"]

# Assets (static files)
[assets]
directory = ".open-next/assets"
binding = "ASSETS"

# KV Namespace (optional - for caching)
[[kv_namespaces]]
binding = "NEXT_CACHE_WORKERS_KV"
id = "your-kv-namespace-id"

# Environment variables
[vars]
NEXT_PUBLIC_API_URL = "https://api.example.com"

# Secrets (set via CLI)
# wrangler secret put SUPABASE_SERVICE_KEY

Cấu hình next.config.js

// next.config.js
import { setupDevPlatform } from '@cloudflare/next-on-pages/next-dev';

/** @type {import('next').NextConfig} */
const nextConfig = {
  // Bắt buộc cho Cloudflare
  output: 'standalone',

  // Experimental features for edge
  experimental: {
    // Enable if needed
  },

  // Image optimization
  images: {
    loader: 'custom',
    loaderFile: './lib/image-loader.ts',
  },
};

// Enable Cloudflare bindings in development
if (process.env.NODE_ENV === 'development') {
  await setupDevPlatform();
}

export default nextConfig;

Build Process

Package.json scripts

{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "build:worker": "opennextjs-cloudflare",
    "preview": "wrangler dev",
    "deploy": "wrangler deploy",
    "deploy:staging": "wrangler deploy --env staging",
    "deploy:production": "wrangler deploy --env production"
  }
}

Build steps

# 1. Build Next.js
npm run build

# 2. Build OpenNext (creates .open-next folder)
npm run build:worker

# 3. Preview locally
npm run preview

# 4. Deploy to Cloudflare
npm run deploy

Thư mục .open-next

Sau khi build, OpenNext tạo:

.open-next/
├── worker.js              # Main worker entry
├── assets/                # Static files (CSS, JS, images)
│   ├── _next/
│   │   ├── static/
│   │   └── ...
│   └── ...
├── cache/                 # Build cache
└── server-functions/      # Server handlers
    └── default/
        └── index.mjs

Cloudflare Bindings trong Next.js

Truy cập bindings

// app/api/example/route.ts
import { getRequestContext } from '@cloudflare/next-on-pages';

export const runtime = 'edge';

export async function GET() {
  // Lấy Cloudflare context
  const { env, ctx } = getRequestContext();

  // Sử dụng KV
  const cached = await env.MY_KV.get('key');

  // Sử dụng Queue
  await env.MY_QUEUE.send({ data: 'test' });

  // Sử dụng R2
  const object = await env.MY_R2.get('file.txt');

  return Response.json({ cached });
}

Type definitions

// env.d.ts
interface CloudflareEnv {
  MY_KV: KVNamespace;
  MY_QUEUE: Queue<QueueMessage>;
  MY_R2: R2Bucket;
  SUPABASE_URL: string;
  SUPABASE_ANON_KEY: string;
}

declare module '@cloudflare/next-on-pages' {
  export function getRequestContext(): {
    env: CloudflareEnv;
    ctx: ExecutionContext;
  };
}

Local Development

# Build first
npm run build:worker

# Run with Wrangler
npm run preview
# → http://localhost:8787

Option 2: next dev với mock bindings

// lib/get-env.ts
import { getRequestContext } from '@cloudflare/next-on-pages';

export function getCloudflareEnv() {
  if (process.env.NODE_ENV === 'development') {
    // Mock bindings for development
    return {
      MY_KV: createMockKV(),
      MY_QUEUE: createMockQueue(),
    };
  }

  return getRequestContext().env;
}

Troubleshooting

Lỗi thường gặp

1. Module not found

Error: Cannot find module 'fs'

→ Package không compatible với Edge. Tìm alternative.

2. Memory exceeded

Error: Worker exceeded memory limit

→ Giảm payload size, dùng streaming.

3. Timeout

Error: Worker exceeded CPU time limit

→ Offload to Queue hoặc Supabase Edge Function.

4. Build fails

# Clear cache và rebuild
rm -rf .next .open-next node_modules/.cache
npm run build && npm run build:worker

Hands-on Exercise

Setup dự án mới

# 1. Create Next.js app
npx create-next-app@latest my-app --typescript --tailwind --app

# 2. Install dependencies
cd my-app
npm install @opennextjs/cloudflare

# 3. Create config files
# (copy các file config từ slides)

# 4. Build và test
npm run build
npm run build:worker
npm run preview

# 5. Visit http://localhost:8787

Tổng kết

Setup Checklist

  • [ ] Install @opennextjs/cloudflare
  • [ ] Create open-next.config.ts
  • [ ] Configure wrangler.toml
  • [ ] Update next.config.js với output: 'standalone'
  • [ ] Add build scripts to package.json
  • [ ] Create type definitions for bindings

Key Files

project/
├── open-next.config.ts    # OpenNext config
├── wrangler.toml          # Cloudflare config
├── next.config.js         # Next.js config
├── env.d.ts               # Type definitions
└── .open-next/            # Build output

Q&A

  1. Có vấn đề gì khi setup không?
  2. Package nào cần thay thế trong dự án của bạn?
  3. Cần bindings nào (KV, Queue, R2)?