npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@nextrush/adapter-edge

v1.0.0

Published

Edge runtime adapter for NextRush (Cloudflare Workers, Vercel Edge, etc.)

Downloads

23

Readme

@nextrush/adapter-edge

Runs a NextRush app on any Fetch API edge runtime -- Cloudflare Workers, Vercel Edge Functions, and Netlify Edge Functions -- through one shared handler implementation.

npm version downloads bundle size types ESM only license

| | | | --- | --- | | Purpose | Fetch-API entry points that run a NextRush Application on edge runtimes | | Package type | Adapter | | Status | Stable (implementation) -- but see Support tier below | | Included in nextrush? | No -- standalone install | | Support tier | Internal -- non--node adapter until GA, may change without a major (see ADR-0005) | | Maintenance | Active | | Runtime | Edge -- any runtime implementing the Fetch API (Cloudflare Workers, Vercel Edge, Netlify Edge) | | Requires | Node >=22 for local tooling/build -- ESM-only -- TypeScript >=5.x | | Introduced | v1.0.0 |

Highlights

  • Zero external runtime dependencies -- only @nextrush/{core,errors,runtime,stream,types} (workspace packages)
  • ESM-only, tree-shakable, side-effect-free (sideEffects: false)
  • One shared request runner behind three platform-specific entry points -- Cloudflare, Vercel, and Netlify handlers cannot silently drift from each other
  • Fully typed -- strict TypeScript, zero any

The problem

Edge runtimes agree on the Fetch API (Request in, Response out) but disagree on the surrounding module contract: Cloudflare Workers export { fetch(request, env, ctx) }, Vercel Edge Functions export a plain (request) => Response with export const config = { runtime: 'edge' }, and Netlify Edge Functions export the same plain shape again. Writing a NextRush app against each platform's raw contract by hand means re-deriving request timeout handling, 404/error fallback, and Application boot sequencing three separate times -- and one of those copies quietly drifting is how a platform-specific bug ships.

// TODAY, without this package -- three near-duplicate handlers, easy to drift:
export default {
  fetch(request: Request, env: Env, ctx: ExecutionContext) {
    // boot the app, race a timeout, build a Response... written by hand, three times
  },
};

@nextrush/adapter-edge centralizes that request-handling logic in one internal runner and exposes a thin, platform-shaped wrapper per target.

When to use

Use @nextrush/adapter-edge if:

  • You are deploying a NextRush app to Cloudflare Workers, Vercel Edge Functions, or Netlify Edge Functions.
  • Your app only needs the Web-standard Request/Response/fetch surface -- no Node.js APIs.

Reach for something else if:

[!TIP] Still unsure which of adapter-edge vs. adapter-serverless a given platform needs? See the full "which package do I install?" table -- one shared answer instead of re-deriving it per package.

Installation

pnpm add @nextrush/adapter-edge @nextrush/core
# npm i @nextrush/adapter-edge @nextrush/core
# yarn add @nextrush/adapter-edge @nextrush/core
# bun add @nextrush/adapter-edge @nextrush/core

[!NOTE] Not included in the nextrush meta package. Install it directly when deploying to an edge runtime.

Quick start

// Cloudflare Workers -- src/index.ts
import { createApp } from '@nextrush/core';
import { createCloudflareHandler } from '@nextrush/adapter-edge';

const app = createApp();

app.use(async (ctx) => {
  ctx.json({ message: 'Hello from the edge', runtime: ctx.runtime });
});

export default createCloudflareHandler(app);

The handler boots the Application on the first incoming request (there is no separate listen() step on edge), then reuses that boot for every subsequent request in the same isolate.

Capabilities

Capabilities

  • Three platform entry points, one runner -- createCloudflareHandler, createVercelHandler, and createNetlifyHandler all delegate to the same internal request runner (createFetchHandler is the runner exposed directly, for any other Fetch-API host).
  • Cloudflare env bindings threaded onto context -- createCloudflareHandler's env argument (KV, D1, R2, Durable Objects, Queues, secrets) is exposed as ctx.env, typed via a generic.
  • Cooperative request timeout -- races the handler against a timer; on timeout, aborts ctx.signal and returns 504, so the framework's own bounded timeout fires before the platform kills the isolate.
  • waitUntil for fire-and-forget work -- ctx.waitUntil(promise) extends the request lifetime for background tasks (logging, analytics) when the platform provides an execution context; a no-op otherwise.

Developer experience

  • Fully typed exports, including a compile-time guard (FetchAdapter, AdapterContextFactory) that fails the build if this adapter's shape drifts from the shared cross-adapter contract.

Mental model

Request (Fetch API) --> createXxxHandler --> shared request runner --> Application.callback()
                                                     |
                                                     +-- races Application against a timeout timer
                                                     +-- builds Response via EdgeContext

Rule: every platform-specific handler (createCloudflareHandler, createVercelHandler, createNetlifyHandler) is a thin wrapper around the same internal runner -- there is exactly one request-handling code path to reason about, regardless of platform.

[!TIP] The full request lifecycle (Mermaid) is in ARCHITECTURE.md.

Common tasks

Deploy to Cloudflare Workers

// src/index.ts
import { createApp } from '@nextrush/core';
import { createCloudflareHandler } from '@nextrush/adapter-edge';

interface Env {
  MY_KV: KVNamespace;
}

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

export default createCloudflareHandler<Env>(app);
# wrangler.toml
name = "my-nextrush-worker"
main = "src/index.ts"
compatibility_date = "2025-01-01"

Deploy to Vercel Edge Functions

// 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' });
});

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

Deploy to Netlify Edge Functions

// 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);

Read the request body across runtimes

app.use(async (ctx) => {
  const text = await ctx.bodySource.text();
  const json = await ctx.bodySource.json();
  const buffer = await ctx.bodySource.buffer();
  const stream = ctx.bodySource.stream();
});

Handle errors with a custom handler

const handler = createCloudflareHandler(app, {
  onError: (error, ctx) => {
    console.error({ error: error.message, path: ctx.path });
    return new Response(JSON.stringify({ error: 'Internal Server Error' }), {
      status: 500,
      headers: { 'Content-Type': 'application/json' },
    });
  },
});

API overview

| Export | Signature | Since | Stability | Description | | ------ | --------- | ----- | --------- | ------------ | | createFetchHandler | (app: Application, options?: FetchHandlerOptions) => FetchHandler | 1.0.0 | Internal | Generic Fetch-API handler; the runner every other export delegates to | | createCloudflareHandler | <Env>(app: Application, options?: FetchHandlerOptions) => { fetch: CloudflareFetchHandler<Env> } | 1.0.0 | Internal | Cloudflare Workers module export -- (request, env, ctx) signature; threads env onto ctx.env | | createVercelHandler | (app: Application, options?: FetchHandlerOptions) => FetchHandler | 1.0.0 | Internal | Vercel Edge Function handler (delegates to createFetchHandler) | | createNetlifyHandler | (app: Application, options?: FetchHandlerOptions) => FetchHandler | 1.0.0 | Internal | Netlify Edge Function handler (delegates to createFetchHandler) | | createHandler | same as createFetchHandler | 1.0.0 | Internal | Alias of createFetchHandler, kept for naming consistency with other adapters | | DEFAULT_EDGE_TIMEOUT_MS | 24_000 | 1.0.0 | Internal | Default request timeout in ms when options.timeout is omitted | | detectEdgeRuntime | () => EdgeRuntimeInfo | 1.0.0 | Internal | Detects which edge platform is running -- see Runtime detection below | | EdgeContext | class | 1.0.0 | Internal | Context implementation for edge runtimes | | createEdgeContext | <Env>(request, executionContext?, trustProxy?, env?) => EdgeContext<Env> | 1.0.0 | Internal | Constructs an EdgeContext directly | | type FetchHandler | (request: Request, ctx?: EdgeExecutionContext) => Response \| Promise<Response> | 1.0.0 | Internal | The handler signature every createXxxHandler returns | | type EdgeExecutionContext | { waitUntil, passThroughOnException? } | 1.0.0 | Internal | Platform execution-context shape consumed by ctx.waitUntil | | type EdgeRuntimeInfo | { runtime, isCloudflare, isVercel, isNetlify, isGenericEdge } | 1.0.0 | Internal | Result shape of detectEdgeRuntime() | | HttpError | re-exported from @nextrush/errors | 1.0.0 | Internal | Uniform error class across all adapters |

Options

FetchHandlerOptions, accepted by every createXxxHandler:

| Option | Type | Required | Default | Security-sensitive | Description | | ------ | ---- | -------- | ------- | ------------------- | ------------ | | onError | (error: Error, ctx: EdgeContext) => Response \| Promise<Response> | No | built-in JSON 500 | -- | Custom error handler; receives the thrown error and the in-flight context | | timeout | number | No | 24000 (DEFAULT_EDGE_TIMEOUT_MS) | -- | Request timeout in ms; races the handler and returns 504 on expiry, aborting ctx.signal. 0 disables the framework timeout (the platform's own limit still applies) | | platform | PlatformId | No | detected (see below) | -- | 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/Netlify does not need it |

Platform reporting (ctx.platform)

ctx.runtime answers "what kind of runtime am I on" ('edge' here, on every host). ctx.platform answers the different question "which named deployment platform is this", typed as PlatformId | undefined where PlatformId is 'lambda' | 'gcf' | 'azure' | 'cloudflare-workers' | 'vercel-edge' | 'netlify-edge'.

| Host | ctx.runtime | ctx.platform | How it is resolved | | ----- | ------------- | -------------- | ------------------- | | Cloudflare Workers | 'cloudflare-workers' | 'cloudflare-workers' | Auto-detected by detectPlatform() | | Vercel Edge | 'vercel-edge' | 'vercel-edge' | Auto-detected | | Netlify Edge | 'edge' | 'netlify-edge' | Auto-detected | | Any other Fetch host | 'edge' | undefined | Nothing matched, and nothing was passed | | Lambda / GCF / Azure via @nextrush/adapter-serverless | 'edge' | 'lambda' / 'gcf' / 'azure' | Passed explicitly by that package's Tier-1 handler |

detectPlatform() reuses detectEdgeRuntime()'s exact three named-platform probes and has no serverless branch: a FaaS platform is never guessed here, only ever declared through FetchHandlerOptions.platform. An explicit value always wins over detection. ctx.platform was added additively in RFC-026; ctx.runtime's type and values are unchanged.

Diagnostics

Three development-mode diagnostics exist to make otherwise-silent failures attributable. All three are gated on app.isProduction being false, except the timeout log, which always fires.

ctx.waitUntil() with no execution context — the promise is dropped without running (unchanged behavior). Outside production the first such call per context warns:

[nextrush/edge] ctx.waitUntil() was called, but this platform provided no execution context — the promise was dropped without running. This is expected on platforms with no background-task support (e.g. Vercel Edge); on Cloudflare Workers it usually means ctx was not passed through to createCloudflareHandler. (This message appears in development only.)

A second, different Application booting in the same isolate — the mechanical signature of building the app inside the exported handler instead of at module scope. Warns once per module instance, outside production:

[nextrush/edge] A different Application booted in this process/isolate than the one that booted first. This usually means createApp() (or createFetchHandler/createCloudflareHandler/createVercelHandler/createNetlifyHandler) is being called inside the exported handler instead of at module scope — rebuilding the app on every invocation defeats warm-instance reuse and increases cold-start-like latency on every request. Build the app once, at module scope, above the export. (This message appears in development only.)

Timeout attribution — when the timeout race wins, app.logger.warn records the effective timeout, whether it came from the default or an explicit option, and the request:

[nextrush/edge] Request timed out after 24000ms (default) — returning 504. GET /slow

The source reads default when options.timeout was omitted and explicit options.timeout when it was supplied.

ctx.ip reads empty with trustProxy off — Edge has no raw socket to fall back to, so ctx.ip is only ever populated from a proxy header (CF-Connecting-IP, X-Forwarded-For, …), which is only trusted when createApp({ proxy: true }) was set. Reading ctx.ip while proxy is unset returns '', and warns once per context, outside production:

[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. If this app runs behind a trusted proxy (e.g. Cloudflare, which sets cf-connecting-ip), pass { proxy: true } to createApp() to resolve it from headers. (This message appears in development only.)

See createApp's proxy option in @nextrush/core's README for what setting it does elsewhere in the framework; this warning exists only on this adapter, since Node/Bun/Deno have a real socket to fall back to and never hit an unconditionally-empty ip.

Runtime detection

detectEdgeRuntime() (re-exported from @nextrush/runtime) answers "which edge platform am I on", distinct from the generic detectRuntime(). Verified against packages/runtime/src/detection.ts, it checks exactly three platforms in order and has no AWS/Lambda branch:

  1. Cloudflare Workers -- navigator.userAgent contains 'Cloudflare-Workers' -> runtime: 'cloudflare-workers', isCloudflare: true.
  2. Vercel Edge -- process.env.VERCEL_REGION is defined -> runtime: 'vercel-edge', isVercel: true.
  3. Netlify Edge -- a Deno global exists AND process.env.NETLIFY === 'true' (Netlify Edge Functions run on Deno under the hood) -> runtime: 'edge' (not a distinct string), isNetlify: true.

If none of the three match, runtime stays at its initial value of 'edge' and isGenericEdge is true. There is no fourth branch for AWS Lambda or any other FaaS platform -- @nextrush/adapter-serverless builds its own handler on top of this package's fetch runner and inherits this same detection, so ctx.runtime reports 'edge' there too (see @nextrush/adapter-serverless's own docs for that package's scope).

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

const info = detectEdgeRuntime();
// Cloudflare Workers: { runtime: 'cloudflare-workers', isCloudflare: true,  isVercel: false, isNetlify: false, isGenericEdge: false }
// Vercel Edge:        { runtime: 'vercel-edge',        isCloudflare: false, isVercel: true,  isNetlify: false, isGenericEdge: false }
// Netlify Edge:        { runtime: 'edge',              isCloudflare: false, isVercel: false, isNetlify: true,  isGenericEdge: false }
// Anything else:       { runtime: 'edge',              isCloudflare: false, isVercel: false, isNetlify: false, isGenericEdge: true }

Result is cached after the first call within a given isolate.

Compatibility

Requirements

| Requirement | Version | | ----------- | ------- | | NextRush | 3.x (@nextrush/core) | | Node.js (local build/test only) | >=22 | | TypeScript | >=5.x |

Runtimes

| Runtime | Supported | Notes | | ------- | --------- | ------ | | Cloudflare Workers | Yes | via createCloudflareHandler | | Vercel Edge Functions | Yes | via createVercelHandler | | Netlify Edge Functions | Yes | via createNetlifyHandler (runs on Deno under the hood) | | Node.js | No | use @nextrush/adapter-node |

Integration

  • Peer dependencies: none -- direct workspace dependencies on @nextrush/{core,errors,runtime,stream,types}.
  • Works with: any NextRush middleware that only touches the Context API (no raw Node req/res access).
  • Built on by: @nextrush/adapter-serverless, which wraps this package's fetch handler for FaaS platforms (AWS Lambda, Google Cloud Functions).

[!IMPORTANT] NextRush is ESM-only, permanently -- no CommonJS build. See the Module Format Policy.

Limitations

Edge runtimes share constraints this package does not paper over:

  • No file system access -- use platform storage (R2, KV) or an external store.
  • No native Node.js modules or native addons -- Web API equivalents only.
  • CPU time and memory limits vary by platform -- consult the platform's own documentation.
  • Large payloads should be streamed rather than buffered in memory.

FAQ

Does ctx.runtime tell me I am on AWS Lambda? No. detectEdgeRuntime() has no Lambda branch; it only distinguishes Cloudflare, Vercel, and Netlify, defaulting to the generic 'edge' otherwise. @nextrush/adapter-serverless is built on this package and inherits the same detection, so Lambda deployments also report runtime: 'edge' -- verified in packages/runtime/src/detection.ts. Use ctx.platform, which serverless's Tier-1 handlers set explicitly ('lambda', 'gcf', 'azure'), to identify the provider.

Why is this package tier "Internal" instead of "Public"? Per ADR-0005, non--node adapters (bun, deno, edge, serverless) stay Internal until each is declared GA; @nextrush/adapter-node is the only adapter in the Public-core tier today.

Does it work without @nextrush/router? Yes -- app.use() alone is enough for simple handlers. Add @nextrush/router only when you need dynamic route matching.

Can I use it outside Cloudflare/Vercel/Netlify? Yes -- createFetchHandler (and its alias createHandler) is a plain (request, ctx?) => Response function usable on any host that speaks the Fetch API, not only the three named platforms.

Package relationships

                        depends on            @nextrush/{core,errors,runtime,stream,types}
@nextrush/adapter-edge -------------->
                        often used with       @nextrush/router
                        usually used next     @nextrush/adapter-serverless (for FaaS platforms)

Architecture

Maintaining or contributing to this package? The internal design -- the shared request runner, context construction, and the compile-time adapter-contract guards -- is in ARCHITECTURE.md. Design history: RFC-013 (adapter contract), ADR-0010 (cross-runtime parity hardening).

Resources


MIT (c) Tanzim Hossain