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

  1. Node.js 20+ and npm:

    node --version  # Should be v20 or higher
    npm --version
    
  2. AWS CLI:

    aws --version
    aws configure
    
  3. TypeScript (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 runtime
  • lib/index.min.mjs — ESM module
  • lib/index.min.umd.js — UMD bundle
  • lib/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 formatting
  • tsconfig.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:

  1. TypeScript Compilation (via Rollup + rollup-plugin-typescript)

    • src/**/*.ts → JavaScript
    • Uses tsconfig.json (ES2020 target)
    • Preserves module syntax for bundling
  2. Bundling (via Rollup)

    • Merges all imports into single file
    • Tree-shakes unused code
    • Minified (via rollup-plugin-terser)
  3. Type Definitions (via rollup-plugin-dts)

    • Merges all .d.ts files
    • Outputs lib/index.d.ts

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:

  1. Set breakpoints in src/
  2. Press F5 to start debugging
  3. 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:

  1. Deploy to AWS: npm run serverless:deploy
  2. Test in production: Invoke deployed functions via AWS CLI
  3. Continuous deployment: Set up GitHub Actions CI/CD

results matching ""

    No results matching ""