Deployment
Mantle bundles Lambda functions with esbuild and deploys infrastructure with OpenTofu — the mantle build and mantle deploy commands handle both.
Quick start
mantle build # bundle all Lambdas
mantle deploy --stage staging # generate infra, plan, applyAlways specify --stage. Running mantle deploy without --stage defaults to dev, which has no tfvars and will prompt interactively.
mantle build
Bundles all Lambda functions under src/lambdas/ using esbuild. Output goes to build/lambdas/<FunctionName>/index.mjs.
mantle build # production build (minified)
mantle build --watch # watch mode — rebuilds on change
mantle build --function ItemsGet # single function
mantle build --sourcemap --no-minify # debug build
mantle build --analyze # emit bundle size metafiles| Flag | Description |
|---|---|
-w, --watch | Watch for changes and rebuild incrementally |
-f, --function <name> | Build a specific function only |
--analyze | Generate esbuild metafiles for bundle analysis |
--sourcemap | Include source maps in the output |
--no-minify | Skip minification (useful for debugging) |
For each Lambda file discovered in src/lambdas/, the build:
- Bundles the handler and all imports into a single
.mjsfile using esbuild - Tree-shakes unused code
- Minifies (unless
--no-minifyis passed) - Writes output to
build/lambdas/<FunctionName>/index.mjs
Entry-point discovery follows file-system routing conventions: api/<name>.<method>.ts, eventbridge/<Name>/index.ts, scheduled/<Name>/index.ts, and standalone/<Name>/index.ts.
mantle deploy
Generates Terraform configuration, then runs tofu init / tofu plan / tofu apply.
mantle deploy --stage staging
mantle deploy --stage production| Flag | Description |
|---|---|
--stage <env> | Target stage — required (dev, staging, production) |
What happens under the hood
- Reads
mantle.config.tsand scanssrc/lambdas/ - Generates
.tffiles for each Lambda, API Gateway routes, EventBridge rules, and scheduled rules - Auto-wires environment variables detected via
getRequiredEnv()/getOptionalEnv()calls - Runs
tofu init(first time only),tofu plan, thentofu apply - Injects infrastructure-derived values (endpoints, ARNs, table names) as Lambda environment variables
Infrastructure generation is safe to run repeatedly — it skips ejected files.
mantle dev
Starts a local HTTP server that simulates API Gateway, rebuilds on file changes, and re-imports handlers on each request (module cache-busting via timestamp query parameter).
mantle dev # http://localhost:3000
mantle dev --port 8080
mantle dev --localstack # route AWS SDK calls to LocalStack| Flag | Default | Description |
|---|---|---|
-p, --port <port> | 3000 | Port to listen on |
--localstack | false | Route AWS SDK calls to LocalStack at http://localhost:4566 |
How it works
- Runs an initial
mantle build(unminified, with source maps) - Starts an HTTP server that routes requests to the matching Lambda handler
- Watches
src/lambdas/with chokidar; rebuilds only the changed function on each file change - On each request, imports the built
.mjsfile with a cache-busting query string so changes take effect immediately without restarting the server
LocalStack integration
When --localstack is passed, the dev server sets:
AWS_ENDPOINT_URL=http://localhost:4566
AWS_ACCESS_KEY_ID=test
AWS_SECRET_ACCESS_KEY=testThis routes all AWS SDK calls (DynamoDB, S3, SQS, etc.) to a locally running LocalStack instance. Start LocalStack separately before running mantle dev --localstack.
CORS in dev
If your mantle.config.ts includes a cors configuration, the dev server respects it for preflight (OPTIONS) requests:
// mantle.config.ts
export default defineConfig({
name: 'my-api',
cors: {
origins: ['http://localhost:5173'],
methods: ['GET', 'POST', 'DELETE'],
headers: ['Content-Type', 'Authorization'],
},
})Database migrations
After tofu apply, run migrations against the live database:
mantle db migrateThe command auto-detects the DSQL endpoint from OpenTofu state when infra/.terraform exists — no manual endpoint configuration needed after the first apply.
Environment variables
Use @mantleframework/env instead of raw process.env:
import { getRequiredEnv, getOptionalEnv } from '@mantleframework/env'
const endpoint = getRequiredEnv('DSQL_ENDPOINT') // throws if missing
const debug = getOptionalEnv('DEBUG_MODE', 'false') // returns default if missingThe following environment variables are used by various Mantle packages:
| Variable | Used by | Description |
|---|---|---|
AWS_REGION | All packages | AWS region for SDK clients |
DSQL_ENDPOINT | @mantleframework/database | Aurora DSQL cluster endpoint |
DATABASE_URL | @mantleframework/database | Neon / Aurora v2 connection string |
API_BEARER_TOKEN | @mantleframework/validation | Static bearer token for auth: 'bearer' |
CORS_ALLOWED_ORIGINS | @mantleframework/core | Comma-separated allowed origins |
IDEMPOTENCY_TABLE_NAME | @mantleframework/resilience | DynamoDB table for idempotency |
EVENT_BUS_NAME | @mantleframework/aws | EventBridge custom bus name |
POWERTOOLS_SERVICE_NAME | @mantleframework/observability | Service name for Powertools |
LOG_LEVEL | @mantleframework/observability | Logger level (DEBUG, INFO, etc.) |
The Lambda module injects ENVIRONMENT with the short token (dev, staging, prod) — use these tokens in any environment-conditional logic.
OpenTofu modules inject the infrastructure-derived values (ARNs, endpoints, table names) automatically as Lambda environment variables.
Next steps
- Handlers — file-system routing and handler factories
- Infrastructure — Terraform module layout and customisation
- Database — migrations, schema, and DSQL configuration