@tora-dev/sluice
v0.1.4
Published
Bundle Serverless Framework functions into a single Lambda with a router for PR preview deploys and local dev
Downloads
4,228
Readme
sluice
Bundle Serverless Framework functions into a single Lambda with a router. Designed for PR preview deploys and local dev — avoids deploying N separate Lambda functions per commit.
Install
npm install @tora-dev/sluiceFor TypeScript handler projects, you also need tsx as a peer:
npm install -D tsxCLI
Print routes
sluice routes --config path/to/serverless.ymlLocal dev server
Starts an HTTP server that dispatches requests to your Lambda handlers using fake API Gateway v2.0 events.
sluice dev --config path/to/serverless.yml --port 3000Handlers are loaded lazily on first request via require(). The dev server automatically:
- Strips URL prefixes detected from
custom.serverless-offline.prefixorcustom.customDomainconfig - Injects
AWS_REGIONandAWS_DEFAULT_REGIONfromprovider.region - Sets
provider.environmentvariables (won't overwrite existing env vars) - Applies per-function environment overrides for the duration of each request
- Adds CORS headers to all responses and handles OPTIONS preflight
- Hot reload — watches the handler directory (and any
--watchdirectories) for source file changes. On change, flushes therequire.cachefor all modules in watched directories so the next request picks up the new code. No server restart needed.
TypeScript handlers
If your handlers are TypeScript files, run sluice with tsx as the interpreter:
{
"scripts": {
"sluice:dev": "tsx node_modules/@tora-dev/sluice/dist/cli.js dev --config path/to/serverless.yml --port 3000"
}
}Plain sluice uses the node shebang and can't import .ts files natively.
Resolve environment variables
Resolves all Serverless Framework variable syntax (${self:...}, ${env:...}, ${file(...):...}, ${ssm:...}) in provider.environment and per-function environments, then writes a .env file.
sluice resolve-env --config path/to/serverless.yml --stage dev --out .env.devSupports --skip-ssm to resolve everything except SSM parameters (useful for fast iteration without AWS credentials). Unresolvable variables are commented out in the output.
Multi-stage workflow
Generate separate .env files per stage and start the dev server against each:
{
"scripts": {
"sluice:resolve:dev": "sluice resolve-env --config lambda/my-service/serverless.yml --stage dev --out .env.dev",
"sluice:resolve:prod": "sluice resolve-env --config lambda/my-service/serverless.yml --stage prod --out .env.prod",
"sluice:serve:dev": "tsx node_modules/@tora-dev/sluice/dist/cli.js dev --config lambda/my-service/serverless.yml --env-file .env.dev --stage dev --port 3000 --watch ../common/src",
"sluice:serve:prod": "tsx node_modules/@tora-dev/sluice/dist/cli.js dev --config lambda/my-service/serverless.yml --env-file .env.prod --stage prod --port 3001 --watch ../common/src"
}
}Generate router
Emits a TypeScript file that maps routes to handler imports. Useful if you want to customize the router or inspect it.
sluice generate --config path/to/serverless.yml --out sluice-router.tsBuild for deploy
Bundles all handlers into a single file using esbuild and generates a SAM template.
sluice build --config path/to/serverless.yml --out .sluice-buildThen deploy with SAM:
sam deploy \
--template-file .sluice-build/template.yaml \
--stack-name sluice-my-service-pr-123 \
--capabilities CAPABILITY_IAM \
--resolve-s3Tear down when the PR closes:
sam delete --stack-name sluice-my-service-pr-123 --no-promptsOptions
| Flag | Default | Description |
|------|---------|-------------|
| --config, -c | ./serverless.yml | Path to serverless.yml |
| --port, -p | 3000 | Dev server port |
| --stage, -s | dev | Stage name for function naming and variable resolution |
| --prefix | auto-detected | URL prefix to strip |
| --watch | none | Extra directories to watch for hot reload (repeatable, dev only) |
| --env-file | .env in config directory | Load environment variables from a file |
| --region | from AWS config | AWS region for SSM lookups |
| --skip-ssm | off | Skip SSM parameter resolution (resolve-env only) |
| --out, -o | varies | Output path |
| --base-path | directory of serverless.yml | Handler source directory |
| --stack-name | sluice-<service> | CloudFormation stack name |
| --minify | off | Minify the bundle |
| --external | @aws-sdk/* | Comma-separated externals |
Programmatic API
import { parseServerlessYml, startDevServer, buildAndPackage, resolveEnvironment, writeEnvFile } from '@tora-dev/sluice';
const routeTable = parseServerlessYml('serverless.yml');
// dev server
await startDevServer({ routeTable, handlerBaseDir: '.', port: 3000 });
// build
await buildAndPackage({
routeTable,
handlerBaseDir: '.',
outDir: '.sluice-build',
});
// resolve env and write .env file
const env = await resolveEnvironment({
serverlessFile: 'serverless.yml',
stage: 'dev',
});
writeEnvFile(env, '.env');How it works
- Parse — reads
serverless.yml, extractsfunctions.*.events[].httpApiroutes,provider.region, and environment variables. Serverless variable syntax is stripped for route parsing; useresolve-envto fully resolve variables including SSM parameters. - Generate — emits a router that maps
METHOD /pathto dynamicimport('./handler') - Bundle — esbuild bundles the router + all handlers into one file with tree-shaking
- Deploy — SAM template defines one Lambda with a catch-all
/{proxy+}route
The router caches resolved handler imports after first invocation, so cold start only pays for the handler that's actually called. During dev, file changes in watched directories flush the require.cache so the next request picks up new code without restarting the server.
Limitations
- Only handles
httpApiandhttpevents. Scheduled, SQS, SNS, and stream triggers are ignored. - Nested Serverless variable syntax (
${ssm:param-${sls:stage}}) is partially handled during route parsing — the sanitizer strips from${to the first}, which can leave trailing characters. Environment values affected by this are excluded from the parsed route table. Useresolve-envfor full variable resolution. - The single Lambda gets a union of whatever IAM permissions you configure in SAM, not per-function roles.
queryStringParametersare not currently populated in the fake API Gateway event during dev server mode.
