@nextrush/adapter-edgeEdge
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
README · ARCHITECTURE · see also Adapter Contract
| Package | @nextrush/adapter-edge |
| Status | Stable (implementation) |
| Support tier | Internal — non--node adapter until GA, may change without a major version |
Included in nextrush? | No — standalone install |
| Runtime | Cloudflare 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
| Property | Type | Description |
|---|---|---|
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? | PlatformId | Overrides 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:
| Host | ctx.runtime | ctx.platform | Resolved 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' | undefined | Nothing 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 platformctxwas not threaded intocreateCloudflareHandler.- A second, different
Applicationbooting in the same isolate warns thatcreateApp()is being called inside the exported handler instead of at module scope, which defeats warm-instance reuse. - Reading
ctx.ipwhiletrustProxyis off (the default) returns'', since Edge has no socket to fall back to — only a proxy header, which is only trusted whencreateApp({ 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:
| Feature | Edge 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);Related
- Platforms Overview — Understanding runtime adapters
- @nextrush/adapter-node — Node.js adapter
- @nextrush/adapter-bun — Bun adapter
- @nextrush/adapter-deno — Deno adapter
- @nextrush/runtime — Runtime detection utilities