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.
Where Serverless fits
| Runtime | Best for | Tradeoff |
|---|---|---|
| Node | Production APIs, long-running servers, full ecosystem | Heavier footprint than Bun or edge |
| Bun | Local dev speed, native TypeScript, fast cold start | Younger ecosystem, some node:* gaps |
| Deno | Secure by default, permission model | Smaller package ecosystem |
| Edge | Global low latency, Cloudflare/Vercel | No long-running server, node:* limited |
| Serverless · this page | Pay-per-request, auto-scaling, AWS/GCF/Azure | Cold starts, no persistent state |
What changes from Node
| Same | Different |
|---|---|
createApp() | No listen() — you export a handler, not start a server |
createRouter() | Per-invocation — platform invokes your handler per event |
| Route registration | Cold starts — first invocation pays app.ready() cost |
| Context API | Warm reuse — module-scope construction survives across invocations |
| Middleware | Response 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
nextrushmeta-package does not bundle a runtime adapter — you add@nextrush/adapter-serverlessalongside it.
$ pnpm add nextrush @nextrush/adapter-serverless
Behind the scenes:
createAppandcreateRouterstill come fromnextrush— runtime-independentcreateLambdaHandlercomes from@nextrush/adapter-serverless— the meta-package'slistentargets@nextrush/adapter-nodespecifically- 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:
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 enginectx.runtimereports'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()andcreateLambdaHandler()determines whether warm-instance reuse actually helps.
Keep app construction at module scope, never inside the handler function:
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.tsBaseline complete — a real Lambda invocation. The handler built at module scope is what makes warm reuse work.
What you learned
- ✓
nextrushgives you the runtime-independent half —createApp,createRouter, and route registration are identical on Lambda and Node - ✓
createLambdaHandlercomes from@nextrush/adapter-serverless, not the meta-package — along withcreateGoogleHandlerandcreateAzureHandler - ✓ 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:
- createApp()
- Router
- Handler
- Serverless adapter
- 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 hereNext 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 →
Deploy to AWS Lambda
Function URL or API Gateway, module-scope warm reuse, and response streaming.
Serverless adapter reference
The full @nextrush/adapter-serverless surface — createLambdaHandler(), createLambdaStreamingHandler(), createGoogleHandler(), createAzureHandler(), and the Tier-3 EventMapper API.