Getting StartedChoose Your Runtime

Serverless

Install NextRush on AWS Lambda, Google Cloud Functions, or Azure Functions, and see what warm-instance reuse and per-invocation timeouts mean for your app.

Runtime · Event-driven

Pay-per-request, auto-scaling. Serverless platforms invoke your handler per event and can terminate the execution environment between calls — there's no process to keep alive, only a warm instance you might get to reuse. Same createApp/createRouter API as Node, deployed behind a one-line handler. The tradeoff: cold starts on a fresh instance and a deployment model built around short-lived invocations.

Pay-per-requestAuto-scaling~10 minBeginner

Where Serverless fits

RuntimeBest forTradeoff
NodeProduction APIs, long-running servers, full ecosystemHeavier footprint than Bun or edge
BunLocal dev speed, native TypeScript, fast cold startYounger ecosystem, some node:* gaps
DenoSecure by default, permission modelSmaller package ecosystem
EdgeGlobal low latency, Cloudflare/VercelNo long-running server, node:* limited
Serverless · this pagePay-per-request, auto-scaling, AWS/GCF/AzureCold starts, no persistent state

What changes from Node

SameDifferent
createApp()No listen() — you export a handler, not start a server
createRouter()Per-invocation — platform invokes your handler per event
Route registrationCold starts — first invocation pays app.ready() cost
Context APIWarm reuse — module-scope construction survives across invocations
MiddlewareResponse buffering by default — streaming is a separate handler

"Partial" means the adapter is functionally complete (Beta, Internal tier — see ADR-0005) but not yet verified against the real platform in CI (see the compatibility matrix) — not that it's missing features.

Targeting a different runtime? Node · Bun · Deno · Edge each implement the same Context and Application contract. Not sure which one fits? See the runtime decision guide.

Before you begin

  • ✓ A terminal and a package manager
  • ✓ An AWS account if you plan to deploy
  • ✓ ~10 minutes

Install NextRush and the serverless adapter

  • Why this matters: like the other non-Node runtimes, the nextrush meta-package does not bundle a runtime adapter — you add @nextrush/adapter-serverless alongside it.
$ pnpm add nextrush @nextrush/adapter-serverless

Behind the scenes:

  • createApp and createRouter still come from nextrush — runtime-independent
  • createLambdaHandler comes from @nextrush/adapter-serverless — the meta-package's listen targets @nextrush/adapter-node specifically
  • Covers AWS Lambda Function URLs and both API Gateway payload formats (HTTP API v2, REST API v1) — auto-detects which event shape it received
  • Google Cloud Functions and Azure Functions have their own handlers (createGoogleHandler, createAzureHandler) from the same package

Write a hello-world Lambda handler

Create index.ts:

index.ts
import { createApp, createRouter } from 'nextrush';
import { createLambdaHandler } from '@nextrush/adapter-serverless';

const app = createApp();
const router = createRouter();

router.get('/', (ctx) => {
  ctx.json({ runtime: ctx.runtime, status: 'ok' });
});

app.route('/', router);
export const handler = createLambdaHandler(app);

Deploy behind a Lambda Function URL (or API Gateway) using your usual AWS tooling.

Expected result — curl https://your-function-url.lambda-url.us-east-1.on.aws/ returns {"runtime": "edge", "status": "ok"}

Behind the scenes:

  • createLambdaHandler(app) wires AWS event mappers and reuses the edge adapter's fetch engine
  • ctx.runtime reports 'edge' because the handler is built on @nextrush/adapter-edge's fetch engine
  • The handler shape ((event, context?) => Promise<result>) is the plain AWS Lambda contract — no NextRush-specific deploy step needed

Build the handler at module scope

  • Why this matters: where you call createApp() and createLambdaHandler() determines whether warm-instance reuse actually helps.

Keep app construction at module scope, never inside the handler function:

index.ts
import { createApp, createRouter } from 'nextrush';
import { createLambdaHandler } from '@nextrush/adapter-serverless';

// Module scope — runs once per cold start, reused across every warm invocation.
const app = createApp();
const router = createRouter();
router.get('/', (ctx) => ctx.json({ status: 'ok' }));
app.route('/', router);

export const handler = createLambdaHandler(app);

Don't put mutable request state in module-scope variables. Each invocation builds a fresh Context, so ctx.state, headers, and body never leak between invocations — but a module-scope let you write to yourself will leak, because that variable survives across invocations exactly like the app instance does.

What changes with per-invocation execution

Your handler is deployed. Before moving on, understand what's genuinely different.

Same: the Context API, createApp, createRouter, route registration, and middleware all behave identically to Node. Nothing in your route handler is Lambda-specific, and the same app code moves to Google Cloud Functions or Azure Functions by swapping createLambdaHandler for createGoogleHandler or createAzureHandler.

Different: there is no long-running process — the platform invokes your handler per event and may terminate the environment between invocations. The first invocation on a fresh instance pays a cold-start cost (app.ready() for the first time); warm invocations on the same instance skip it. createLambdaHandler buffers the full response body; for true streaming (lower time-to-first-byte), use createLambdaStreamingHandler instead.

Serverless runtime working — warm reuse and all.

Serverless baseline complete

Confirm the function answers:

curl -i https://your-function-url.lambda-url.us-east-1.on.aws/
# → HTTP/1.1 200 OK
# → {"runtime":"edge","status":"ok"}

Your project now looks like:

my-function/
  package.json
  index.ts

Baseline complete — a real Lambda invocation. The handler built at module scope is what makes warm reuse work.

What you learned

  • nextrush gives you the runtime-independent half — createApp, createRouter, and route registration are identical on Lambda and Node
  • createLambdaHandler comes from @nextrush/adapter-serverless, not the meta-package — along with createGoogleHandler and createAzureHandler
  • ✓ Module-scope construction is what makes warm reuse work — building the app inside the handler re-boots it on every invocation
  • ✓ Response buffering is the default; true streaming uses createLambdaStreamingHandler

The same app, every runtime

The code you just wrote runs on every runtime NextRush supports. Only the adapter import changes:

  1. createApp()
  2. Router
  3. Handler
  4. Serverless adapter
  5. Running
// Node:      import { listen } from '@nextrush/adapter-node';
// Bun:       import { listen } from '@nextrush/adapter-bun';
// Deno:      import { listen } from '@nextrush/adapter-deno';
// Edge:      import { serve } from '@nextrush/adapter-edge';
// Serverless import { handler } from '@nextrush/adapter-serverless';  // ← you are here

Next steps

🚀 Build a Task API — recommended · ~20 min

Go past hello-world on the runtime you set up: routing, a JSON body, and a real 404. Start the tutorial →

Continue learning

Was this helpful?

On this page