@nextrush/adapter-serverlessServerless
Deploy a NextRush app to AWS Lambda, Google Cloud Functions, or Azure Functions in one line.
Run a NextRush app as a serverless function. The adapter separates the execution model (per-invocation, stateless, timeout→504, warm-instance reuse) from each provider's event format — you import one Tier-1 handler and never touch the mapper plumbing underneath.
Source & internals
README · ARCHITECTURE · see also Adapter Contract
| Package | @nextrush/adapter-serverless |
| Status | Beta |
| Support tier | Internal — non--node adapter until GA, may change without a major (ADR-0005) |
Included in nextrush? | No — standalone install |
| Runtime | AWS Lambda, Google Cloud Functions, Azure Functions — built on @nextrush/adapter-edge's fetch engine |
Cloudflare Workers uses a different package
Cloudflare's Tier-1 handler (createCloudflareHandler) ships in
@nextrush/adapter-edge, not here — it
shares the edge execution model, not the serverless one.
Installation
$ pnpm add @nextrush/adapter-serverless
Quick start
import { createApp } from '@nextrush/core';
import { createLambdaHandler } from '@nextrush/adapter-serverless';
const app = createApp();
app.use(async (ctx) => {
ctx.json({ message: 'Hello from Lambda!' });
});
export const handler = createLambdaHandler(app);Tier-1 handlers (what you actually import)
| Function | Platform | Event shapes handled |
|---|---|---|
createLambdaHandler(app, options?) | AWS Lambda | Function URL / API Gateway HTTP API (v2) and REST API (v1) — auto-detected per invocation, no explicit provider needed |
createLambdaStreamingHandler(app, options?) | AWS Lambda response streaming (awslambda.streamifyResponse) | Lambda Function URL streaming |
createGoogleHandler(app, options?) | Google Cloud Functions | HTTP-triggered functions — a (req, res) drop-in for functions.http() |
createAzureHandler(app, options?) | Azure Functions | HTTP-triggered functions (v4 model) — a (req) => response drop-in for app.http() |
// Google Cloud Functions
import { createGoogleHandler } from '@nextrush/adapter-serverless';
import * as functions from '@google-cloud/functions-framework';
functions.http('api', createGoogleHandler(app));// Azure Functions (v4 model)
import { createAzureHandler } from '@nextrush/adapter-serverless';
import { app as functions } from '@azure/functions';
functions.http('api', { handler: createAzureHandler(app) });See Google Cloud Functions deployment and Azure Functions deployment for the full deploy commands and platform-specific configuration these two drop-ins need.
These two signatures changed in 1.0.0-beta.1
Before 1.0.0-beta.1, createGoogleHandler and createAzureHandler took a
normalized event struct that you built from the platform's req yourself. That
behavior is preserved unchanged under the new names
createGoogleEventHandler / createAzureEventHandler — rename your call to
keep an existing hand-written bridge working, or delete the bridge and pass the
drop-in straight to the platform. Reach for the *EventHandler names for
fixture testing, a custom bridge, or a host whose request object is not the
standard SDK shape.
Knowing which platform you are on
ctx.runtime is 'edge' on every provider here — this adapter runs on
@nextrush/adapter-edge's fetch engine, whose runtime detector has no
AWS/GCP/Azure branch. A check for ctx.runtime === 'node' typechecks and never
matches.
ctx.platform is the field that names the provider: 'lambda', 'gcf', or
'azure'. Each Tier-1 handler sets it explicitly, so it is declared rather than
guessed.
import { createLambdaHandler } from '@nextrush/adapter-serverless';
app.use(async (ctx) => {
ctx.json({ platform: ctx.platform, runtime: ctx.runtime });
});
export const handler = createLambdaHandler(app);Each Tier-1 handler accepts ServerlessHandlerOptions:
interface ServerlessHandlerOptions {
/** Per-invocation timeout (ms). Exceeding it produces a 504 result instead of hanging the invocation. */
timeout?: number;
}export const handler = createLambdaHandler(app, { timeout: 5000 });Advanced: building a custom provider (Tier 3)
See Event mapping for how the built-in mappers above translate a
platform event into the same Request/Response every adapter uses — the mental model for what
you're implementing below.
Application developers should stop at the Tier-1 handlers above. Implement
EventMapper only to add a platform NextRush doesn't ship (Oracle, Fly.io,
OpenFaaS, an internal platform):
import { createServerlessAdapter } from '@nextrush/adapter-serverless';
import type { EventMapper } from '@nextrush/adapter-serverless';
const myMapper: EventMapper<MyEvent, MyResult> = {
name: 'my-platform',
toRequest(event) {
/* build a Web Request from the platform event */
},
fromResponse(response, event) {
/* map the Response back to the platform's expected result */
},
};
const adapter = createServerlessAdapter({ mappers: [myMapper] });
export const handler = adapter.createHandler(app);mappers is an immutable, per-adapter registry — there is no global mutable
mapper registry. When more than one mapper is supplied, an explicit
provider name wins; each mapper's optional detect() only runs as a
fallback when no provider is named.
Request Paths and Dot Segments
rawPath is parsed into a URL before dispatch
The Lambda adapter builds its Request from the event's rawPath, and that construction runs the
URL parser — so a dot segment reaches the application as the resolved path rather than as the
bytes the client sent, and the framework's 400 rejection cannot fire.
Authorise path rules against the path the application actually routed on (ctx.path), and reject
unwanted raw targets at API Gateway (WAF, resource policy, routing rules) if you need them
rejected at all. See
Request Paths and Dot Segments.
Troubleshooting
Timeout returns a 504 result, not a thrown error
Exceeding timeout produces a 504-shaped result via the platform's normal
response path — it does not reject the returned promise. Check the
response status your platform's logs show, not a caught exception.
Every mapper guards the one field it cannot translate without and throws a named
[nextrush/serverless] error naming the likely cause (a wrong payload format, or
an incomplete hand-written bridge on the *EventHandler path) instead of a raw
TypeError from framework internals. On GCF, a request whose rawBody is absent
while body is already parsed logs a [nextrush/serverless] warning and omits
the body rather than stringifying an object into it.
Because this adapter runs on @nextrush/adapter-edge's engine, that package's
development-mode diagnostics apply here too: the ctx.waitUntil() no-op warning
(serverless invocations supply no execution context), the boot-reuse warning when
createApp() is called inside the exported handler, and the timeout-attribution
log accompanying every 504. See
@nextrush/adapter-edge for the exact messages.
Related
- Platforms overview
@nextrush/adapter-edge— Cloudflare Workers, Vercel Edge, Netlify Edge- Deployment guides