Working Locally on Your Machine
This guide explains how to develop, test, and debug the AWS Lambda boilerplate directly on your local machine without Docker.
Overview
Local development gives you:
- Fast iteration: Instant TypeScript compilation and test execution
- Debugging: Breakpoints, inspectors, and LSP support
- Direct control: Access to file system, environment variables, AWS credentials
- Simple setup: No Docker overhead for quick changes
Prerequisites
Node.js 20+ and npm:
node --version # Should be v20 or higher npm --versionAWS CLI:
aws --version aws configureTypeScript (optional, included in devDependencies):
npm install -g typescript
Initial Setup
1. Install Dependencies
npm install
This installs all dev dependencies including:
- TypeScript
- Rollup bundler
- Jest test framework
- ESLint + Prettier
- Serverless Framework
2. Create .env File (Optional)
For local environment variables:
# .env (not tracked in git)
AWS_PROFILE=local
LOG_LEVEL=debug
STAGE=dev
Then load in your shell:
export $(cat .env | xargs)
Or directly pass to npm:
STAGE=dev npm run build
Development Workflow
1. Edit Code
Modify handler files in src/handlers/:
// src/handlers/hello.ts
export const helloHandler = async (event) => {
console.log('Received event:', JSON.stringify(event));
// Your logic here
};
2. Build (TypeScript → JavaScript)
Compile TypeScript and bundle via Rollup:
npm run build
Output:
lib/index.min.cjs— CommonJS for Lambda runtimelib/index.min.mjs— ESM modulelib/index.min.umd.js— UMD bundlelib/index.d.ts— TypeScript definitions
3. Test Locally
Run Jest test suite:
npm run test:run
Output:
- Test results in console
- Coverage report in
dist/coverage/ - HTML coverage report:
dist/coverage/index.html
4. Lint & Format
Check for code style issues:
# Check only
npm run lint:run
# Auto-fix
npm run lint:fix
Configuration files:
.eslintrc.json— ESLint rules.prettierrc.json— Code formattingtsconfig.json— TypeScript compiler options
5. Test Specific Handler
Before full test suite, test a single handler:
# Test direct invocation handler
npm run serverless:invoke:local
# Test API Gateway handler
npm run serverless:invoke:local:api
These use Serverless Framework's local invocation, which simulates actual Lambda/API Gateway payloads.
Understanding the Build Process
TypeScript → JavaScript
npm run build
Steps:
TypeScript Compilation (via Rollup + rollup-plugin-typescript)
src/**/*.ts→ JavaScript- Uses
tsconfig.json(ES2020 target) - Preserves module syntax for bundling
Bundling (via Rollup)
- Merges all imports into single file
- Tree-shakes unused code
- Minified (via rollup-plugin-terser)
Type Definitions (via rollup-plugin-dts)
- Merges all
.d.tsfiles - Outputs
lib/index.d.ts
- Merges all
Why Rollup?
- Better for libraries (one entry point)
- Smaller bundle size
- Output formats: CommonJS, ESM, UMD
- Supports tree-shaking
File Structure After Build
lib/
index.min.cjs ← Lambda runtime uses this (CommonJS)
index.min.cjs.map ← Source map for debugging
index.min.mjs ← ES modules version
index.min.umd.js ← UMD for browsers/other
index.d.ts ← TypeScript definitions
Local Invocation (Without AWS Deployment)
Test Direct Invocation Handler
Uses events/hello.invoke.json:
npm run serverless:invoke:local
Event payload:
{
"name": "Bayu"
}
Response:
{
"message": "Hello, Bayu!",
"requestId": "request-direct-123",
"timestamp": "2026-05-12T00:00:00.000Z",
"input": {
"name": "Bayu",
"source": "event"
}
}
Test API Gateway Handler
Uses events/hello.apigw.json:
npm run serverless:invoke:local:api
Event payload (simulated API Gateway):
{
"httpMethod": "GET",
"path": "/hello",
"queryStringParameters": {
"name": "Bayu"
}
}
Response:
{
"statusCode": 200,
"body": "{...}"
}
Custom Event Payload
Invoke with custom payload:
# Create custom event
echo '{"name":"Alice"}' > events/custom.json
# Invoke
serverless invoke local --function helloInvoke --path events/custom.json
Debugging
1. Console Logging
Add logs directly in handlers:
export const helloHandler = async (event) => {
console.log('Event:', event);
console.log('Name:', name);
return { message: `Hello, ${name}!` };
};
View logs during local invocation:
npm run serverless:invoke:local
2. VS Code Debugger
.vscode/launch.json:
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Debug Jest Tests",
"program": "${workspaceFolder}/node_modules/.bin/jest",
"args": ["--runInBand"],
"console": "integratedTerminal"
},
{
"type": "node",
"request": "launch",
"name": "Debug Local Invoke",
"program": "${workspaceFolder}/node_modules/.bin/serverless",
"args": ["invoke", "local", "--function", "helloInvoke"],
"console": "integratedTerminal"
}
]
}
Then:
- Set breakpoints in
src/ - Press F5 to start debugging
- Step through code with F10/F11
3. TypeScript Type Checking
Check for type errors without building:
npx tsc --noEmit
Documentation Generation
Generate API Docs (TypeDoc)
npm run build:docs
Output: dist/docs/index.html
Open in browser:
open dist/docs/index.html
Includes:
- Function signatures
- Parameter types
- Return types
- JSDoc comments from source
Generate HonKit Book
npm run build:docs:book
Output: dist/book/index.html
View with local server:
cd dist/book && python3 -m http.server 8000
# Visit http://localhost:8000
Watch Mode for Continuous Development
Use npm scripts with watch flags:
Watch TypeScript Changes
npm run build -- --watch
Rebuilds automatically as you edit src/.
Watch Tests
npm run test -- --watch
Reruns tests as you edit src/ or test/.
File Editing Tips
TypeScript Language Server (LSP) in VS Code
Install extensions:
- TypeScript Vue Plugin — For TS support
- ESLint — Real-time linting
- Prettier — Format on save
settings.json:
{
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
"eslint.validate": ["typescript"],
"eslint.format.enable": true
}
Import Path Aliases
TypeScript tsconfig.json sets baseUrl: "src", so imports work like:
// Instead of: import { helloHandler } from '../handlers/hello'
import { helloHandler } from 'handlers/hello'
Performance Tips
1. Incremental Type Checking
Only check changed files:
npx tsc --incremental
2. Run Specific Tests
Test only one handler:
npm run test -- hello.spec.ts
3. Skip Linting During Development
Build and test, lint later:
npm run build
npm run test:run
# Then fix linting issues before commit
npm run lint:fix
Common Issues
"Cannot find module" Error
Solution: Rebuild the project:
npm run build
Handlers are exported from lib/index.min.cjs which is generated by Rollup.
TypeScript Compilation Errors
Solution: Check tsconfig.json:
{
"compilerOptions": {
"strict": false, // Disable strict mode for flexibility
"target": "ES2020",
"module": "ES2020",
"rootDir": "./src",
"baseUrl": "./src"
}
}
Jest Tests Failing After Code Changes
Solution: Clear Jest cache:
npm run test:run -- --clearCache
npm run test:run
Port Already in Use
If port 3000/8080 is busy:
# Find process using port 3000
lsof -i :3000
# Kill it
kill -9 <PID>
# Or use a different port
PORT=3001 npm run build:docs:book
Next Steps
Once you're comfortable locally:
- Deploy to AWS:
npm run serverless:deploy - Test in production: Invoke deployed functions via AWS CLI
- Continuous deployment: Set up GitHub Actions CI/CD