ReferenceRuntime Adapters
@nextrush/adapter-edge

Edge

Universal edge runtime adapter for Cloudflare Workers, Vercel Edge, and Netlify Edge.

Connect your NextRush application to edge computing platforms.

Edge runtimes execute code at the network edge, close to users. This adapter provides a universal interface that works across Cloudflare Workers, Vercel Edge Functions, Netlify Edge Functions, and any platform supporting the Web Fetch API.

Source & internals

Package@nextrush/adapter-edge
StatusStable (implementation)
Support tierInternal — non--node adapter until GA, may change without a major version
Included in nextrush?No — standalone install
RuntimeCloudflare Workers, Vercel Edge Functions, Netlify Edge Functions

Edge Runtime Constraints

Edge runtimes lack access to the file system, native modules, and child_process. Review the limitations table before deploying.


Installation

$ pnpm add @nextrush/adapter-edge

Quick Start

The edge adapter creates a standard fetch handler:

import { createApp } from '@nextrush/core';
import { createFetchHandler } from '@nextrush/adapter-edge';

const app = createApp();

app.use(async (ctx) => {
  ctx.json({ message: 'Hello from the Edge!' });
});

export default createFetchHandler(app);

Platform-Specific Handlers

Each edge platform has a dedicated handler factory. Choose the one matching your deployment target.

import { createApp } from '@nextrush/core';
import { createCloudflareHandler } from '@nextrush/adapter-edge';

const app = createApp();

app.use(async (ctx) => {
  ctx.json({
    message: 'Hello from Cloudflare Workers!',
    colo: ctx.raw.req.cf?.colo,
  });
});

export default createCloudflareHandler(app);

Deploy with Wrangler:

wrangler deploy
// api/hello.ts
import { createApp } from '@nextrush/core';
import { createVercelHandler } from '@nextrush/adapter-edge';

const app = createApp();

app.use(async (ctx) => {
  ctx.json({
    message: 'Hello from Vercel Edge!',
    region: process.env.VERCEL_REGION,
  });
});

export const config = { runtime: 'edge' };
export default createVercelHandler(app);
// netlify/edge-functions/api.ts
import { createApp } from '@nextrush/core';
import { createNetlifyHandler } from '@nextrush/adapter-edge';

const app = createApp();

app.use(async (ctx) => {
  ctx.json({ message: 'Hello from Netlify Edge!' });
});

export default createNetlifyHandler(app);

Configure in netlify.toml:

[[edge_functions]]
path = "/api/*"
function = "api"

API Reference

createFetchHandler(app, options?)

Create a universal fetch handler for any edge platform.

import { createFetchHandler } from '@nextrush/adapter-edge';

const handler = createFetchHandler(app, {
  onError: (error, ctx) => {
    console.error('Edge error:', error);
    return new Response('Error', { status: 500 });
  },
});

FetchHandlerOptions

PropertyTypeDescription
onError?(error: Error, ctx: EdgeContext) => Response | Promise<Response>Custom error handler for uncaught exceptions
timeout?number= 24000 (DEFAULT_EDGE_TIMEOUT_MS)Request timeout in milliseconds. Races the handler and returns 504 Gateway Timeout on expiry, aborting ctx.signal. Set to 0 to disable the framework timeout (the platform's own limit still applies).
platform?PlatformIdOverrides the detected value of ctx.platform. Set by @nextrush/adapter-serverless's Tier-1 handlers, which know their provider unambiguously; application code on Cloudflare, Vercel, or Netlify does not need it.

createCloudflareHandler(app, options?)

Create a Cloudflare Workers module export.

import { createCloudflareHandler } from '@nextrush/adapter-edge';

// Returns { fetch: FetchHandler }
export default createCloudflareHandler(app);

createVercelHandler(app, options?)

Create a Vercel Edge Function handler.

import { createVercelHandler } from '@nextrush/adapter-edge';

export const config = { runtime: 'edge' };
export default createVercelHandler(app);

createNetlifyHandler(app, options?)

Create a Netlify Edge Function handler.

import { createNetlifyHandler } from '@nextrush/adapter-edge';

export default createNetlifyHandler(app);

createHandler(app, options?)

Alias for createFetchHandler. Use for generic edge deployments.

import { createHandler } from '@nextrush/adapter-edge';

export default { fetch: createHandler(app) };

Error Handling

Customize error responses for edge environments:

const handler = createFetchHandler(app, {
  onError: (error, ctx) => {
    // Log to edge logging service
    console.error('Request failed:', {
      path: ctx.path,
      error: error.message,
    });

    // Return custom error response
    return new Response(
      JSON.stringify({
        error: 'Service temporarily unavailable',
        requestId: ctx.state.requestId,
      }),
      {
        status: 503,
        headers: { 'Content-Type': 'application/json' },
      }
    );
  },
});

Edge Context

ctx.runtime answers what kind of runtime is executing ('edge' on most hosts). ctx.platform answers which named deployment platform it is, typed PlatformId | undefined:

Hostctx.runtimectx.platformResolved by
Cloudflare Workers'cloudflare-workers''cloudflare-workers'Detection
Vercel Edge'vercel-edge''vercel-edge'Detection
Netlify Edge'edge''netlify-edge'Detection
Any other Fetch host'edge'undefinedNothing matched
Lambda / GCF / Azure'edge''lambda' / 'gcf' / 'azure'Passed explicitly by @nextrush/adapter-serverless

Detection covers exactly the three named edge platforms. A serverless platform is never guessed — it only ever arrives through FetchHandlerOptions.platform, and an explicit value always wins over detection.

EdgeContext also exposes a waitUntil() method for fire-and-forget operations that complete after the response is sent:

app.use(async (ctx) => {
  // Fire-and-forget async operations (logging, analytics)
  ctx.waitUntil(
    fetch('https://analytics.example.com/track', {
      method: 'POST',
      body: JSON.stringify({ path: ctx.path }),
    })
  );

  ctx.json({ ok: true });
});

Development-mode diagnostics

Outside production, three otherwise-silent failure modes announce themselves once:

  • ctx.waitUntil() on a platform that supplies no execution context (Vercel Edge, a serverless invocation) drops the promise without running it, and warns: [nextrush/edge] ctx.waitUntil() was called, but this platform provided no execution context — the promise was dropped without running. On Cloudflare Workers it usually means the platform ctx was not threaded into createCloudflareHandler.
  • A second, different Application booting in the same isolate warns that createApp() is being called inside the exported handler instead of at module scope, which defeats warm-instance reuse.
  • Reading ctx.ip while trustProxy is off (the default) returns '', since Edge has no socket to fall back to — only a proxy header, which is only trusted when createApp({ proxy: true }) is set. Warns once per context: [nextrush/edge] ctx.ip is an empty string because trustProxy is false (the default) — Edge has no socket to fall back to, so no proxy headers means no IP.

Every 504 additionally logs the effective timeout and its source, in production too: [nextrush/edge] Request timed out after 24000ms (default) — returning 504. GET /slow.


Cloudflare Bindings

Cloudflare bindings (KV, D1, R2, secrets) are passed as the second argument to the Workers module fetch handler. createCloudflareHandler receives (request, env, ctx) internally but does not expose env on the NextRush context.

To access bindings, wrap your app setup in a closure:

import { createApp } from '@nextrush/core';
import { createFetchHandler } from '@nextrush/adapter-edge';
import type { FetchHandler } from '@nextrush/adapter-edge';

interface Env {
  MY_KV: KVNamespace;
  MY_DB: D1Database;
}

export default {
  async fetch(request: Request, env: Env, execCtx: ExecutionContext): Promise<Response> {
    const app = createApp();

    app.use(async (ctx) => {
      const value = await env.MY_KV.get('key');
      ctx.json({ value });
    });

    const handler = createFetchHandler(app);
    return handler(request, execCtx);
  },
};

For production use, create the app and handler at module scope and pass env through middleware state to avoid per-request overhead.


Edge Limitations

Edge runtimes have constraints compared to Node.js:

FeatureEdge Support
fs module❌ Not available
child_process❌ Not available
Native modules❌ Not available
WebSocket⚠️ Platform-specific
Long-running tasks⚠️ Time limits apply
Request body✅ Streaming supported
Fetch API✅ Full support

Design your application with these constraints in mind.


Cold Start Optimization

Minimize cold start times:

// ✅ Good: Create app at module level
const app = createApp();
app.use(/* middleware */);

const handler = createFetchHandler(app);
export default { fetch: handler };

// ❌ Avoid: Creating app per request
export default {
  fetch: (req) => {
    const app = createApp(); // Cold start overhead
    return createFetchHandler(app)(req);
  },
};

TypeScript

All exports are fully typed:

import type { Application } from '@nextrush/core';
import type {
  FetchHandler,
  FetchHandlerOptions,
  EdgeExecutionContext,
} from '@nextrush/adapter-edge';
import {
  createFetchHandler,
  createCloudflareHandler,
  createVercelHandler,
  createNetlifyHandler,
  detectEdgeRuntime,
} from '@nextrush/adapter-edge';

const options: FetchHandlerOptions = {
  timeout: 25_000,
  onError: (error, ctx) => new Response('Error', { status: 500 }),
};

const handler: FetchHandler = createFetchHandler(app, options);

Was this helpful?

On this page