Deployments
AWS Lambda
Use the AWS Lambda target for API Gateway HTTP API v2 or Lambda Function URL payload format 2.0. The target emits Lambda-specific artifacts and avoids generating Cloudflare modules for Node-only server dependencies.
Build and package
mreact-router build --target=aws-lambda
mreact-router package aws-lambda --from .mreact --out .lambdaThe package directory should contain the built .mreact/ output, mreact-handler.mjs, package metadata, lockfiles, and production node_modules. Do not deploy the full project checkout with src/, tests, browser tooling, build caches, and dev dependencies. AWS has a 250 MB unzipped package limit, so keep the asset minimal.
With pnpm, install production dependencies into .lambda using a hoisted node linker and verify symlink count as well as byte size before upload.
pnpm --dir .lambda install --prod --frozen-lockfile --ignore-scripts --config.node-linker=hoisted
find .lambda -type l | wc -l
du -sh .lambdaCreate the deployment zip from inside .lambda so the handler, package metadata, production node_modules, and .mreact sit at the archive root.
(
cd .lambda
zip -r ../mreact-lambda.zip .
)
unzip -l mreact-lambda.zip | headGenerated handler
The default generated handler warms middleware during Lambda module initialization, then validates every API Gateway payload before request handling. This keeps initialization work narrow while avoiding first-valid-request contention from unrelated route render modules.
import {
createAwsLambdaRequestHandler,
warmAwsLambdaRuntime,
} from "@reckona/mreact-router/adapters/aws-lambda";
const options = {
importPolicy: "generated",
outDir: ".mreact",
preload: { mode: "middleware" },
} as const;
await warmAwsLambdaRuntime(options);
export const handler = createAwsLambdaRequestHandler({ ...options, preload: "none" });The generated package writes mreact-lambda-artifact.json with "handler": "mreact-handler.handler" and "streamingHandler": "mreact-streaming-handler.handler". Both entries warm the same policy during module initialization; select the streaming entry only for a Lambda Function URL configured for response streaming.
Use a custom handler when you need adapter options such as allowedHosts, serverActions.authorize, onResponse, or a custom error handler. Package it with the handler entry so the bundled output still lands at mreact-handler.mjs.
mreact-router package aws-lambda --from .mreact --out .lambda --handler src/lambda.tsimport { createPreloadedAwsLambdaRequestHandler } from "@reckona/mreact-router/adapters/aws-lambda";
export const handler = await createPreloadedAwsLambdaRequestHandler({
allowedHosts: ["app.example.com"],
importPolicy: "generated",
outDir: ".mreact",
timings: process.env.MREACT_ROUTER_TIMINGS === "1",
});Function URL deployment
For a direct smoke-testable deployment, create or update a Lambda function and attach a Function URL with payload format 2.0. The example below uses the generated non-streaming handler. Use a role that already has the basic Lambda execution permissions for CloudWatch Logs.
aws lambda create-function \
--function-name mreact-app \
--runtime nodejs24.x \
--architectures arm64 \
--role arn:aws:iam::123456789012:role/mreact-lambda-role \
--handler mreact-handler.handler \
--zip-file fileb://mreact-lambda.zip \
--environment "Variables={MREACT_SERVER_ACTION_SECRET=replace-with-a-stable-secret}"
aws lambda create-function-url-config \
--function-name mreact-app \
--auth-type NONE \
--invoke-mode BUFFERED
aws lambda add-permission \
--function-name mreact-app \
--statement-id function-url-public \
--action lambda:InvokeFunctionUrl \
--principal "*" \
--function-url-auth-type NONEFor updates, rebuild the zip and replace the function code and environment together.
aws lambda update-function-code \
--function-name mreact-app \
--zip-file fileb://mreact-lambda.zip
aws lambda update-function-configuration \
--function-name mreact-app \
--environment "Variables={MREACT_SERVER_ACTION_SECRET=replace-with-a-stable-secret,MREACT_ROUTER_TIMINGS=1}"Function URLs do not give you a stable custom domain by themselves. Use a custom domain in front of the function, or configure the adapter allowedHosts for the hostname that users actually request when absolute URLs, redirects, metadata, or auth callbacks depend on the public origin.
Smoke-test the deployed URL before routing real traffic.
FUNCTION_URL=$(aws lambda get-function-url-config --function-name mreact-app --query FunctionUrl --output text)
FUNCTION_URL=${FUNCTION_URL%/}
curl -fsS "$FUNCTION_URL/"
curl -fsS "$FUNCTION_URL/api/health"
curl -sS -o /dev/null -w "%{http_code}\n" "$FUNCTION_URL/not-found-check"Replace /api/health with a lightweight route your app provides. The not-found check should print 404. If your app uses server actions, submit one deployed form after every release because the form token, nonce cookie, and MREACT_SERVER_ACTION_SECRET must agree across instances.
Cold start and preload
Lambda handlers materialize runtime files under /tmp by default while treating deployed .mreact output as read-only. Generated handlers warm middleware during initialization by default; select hot-route-requests, all, or none with --aws-lambda-preload only after measuring the packaged handler, and pair hot-route-requests with a comma-separated route list such as --aws-lambda-preload-routes=/,/login. none disables generated module preload but still materializes the writable runtime directory during generated handler import. The package command preserves the policy written by the build unless an explicit package option overrides it. Direct handlers keep preload conservative so simple middleware responses, health checks, and loader redirects do not eagerly load page render artifacts. Stream routes also settle the loader's outer control result before loading page render artifacts or committing a response, including when a loading boundary exists or the control result comes from an imported helper. Return non-critical promises through defer() and render them with <Await> when they should continue behind the loading boundary. Ordinary handlers, including createPreloadedAwsLambdaRequestHandler(), validate each event before they start implicit runtime work.
Measure before changing preload. Compare cold start, first request, and warm request timings for the same route and Lambda memory size.
Production settings
- Set
MREACT_SERVER_ACTION_SECRETconsistently across every function instance when server action forms can be rendered by one instance and submitted to another. - Configure
allowedHostsor the equivalent custom-domain policy when absolute URLs, redirects, metadata, or auth callbacks depend on the public origin. - Keep database clients, SDKs, and secrets in server-only modules.
- Upload hidden source maps from CI when client stack traces need symbolication.
- Use Logging and Diagnostics temporarily for adapter timings while diagnosing latency.
- Keep the unzipped
.lambdaartifact under AWS Lambda's size limits and check bothdu -sh .lambdaand the final zip size before upload.