Lab 14: Serverless Node.js

Time: 30 minutes | Level: Advanced | Docker: docker run -it --rm node:20-alpine sh

Overview

Build serverless-ready Node.js: Lambda handler structure, cold start optimization, Lambda layers, event sources (API GW/SQS/DynamoDB Streams), middy middleware, and local testing.


Step 1: Lambda Handler Structure

// handler.js — basic Lambda handler patterns

// Pattern 1: Simple async handler
exports.hello = async (event, context) => {
  console.log('Event:', JSON.stringify(event));
  console.log('Context:', {
    functionName: context.functionName,
    awsRequestId: context.awsRequestId,
    remainingTimeMs: context.getRemainingTimeInMillis()
  });

  return {
    statusCode: 200,
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ message: 'Hello from Lambda!', timestamp: Date.now() })
  };
};

// Pattern 2: Error handling
exports.withErrorHandling = async (event) => {
  try {
    const result = await processEvent(event);
    return { statusCode: 200, body: JSON.stringify(result) };
  } catch (err) {
    console.error('Error:', err);

    // Never throw unhandled errors from Lambda handlers
    // Return proper HTTP response instead
    return {
      statusCode: err.statusCode ?? 500,
      body: JSON.stringify({
        error: err.message,
        code: err.code ?? 'INTERNAL_ERROR'
      })
    };
  }
};

// Pattern 3: Initialization outside handler (runs once per container)
const AWS = require('aws-sdk');
const db = require('./db'); // Connection established outside handler!

exports.optimized = async (event) => {
  // db is already connected — no cold start penalty on warm invocations
  const result = await db.query('SELECT 1');
  return { statusCode: 200, body: JSON.stringify(result) };
};

Step 2: Cold Start Optimization


Step 3: Event Sources


Step 4: Middy Middleware


Step 5: Lambda Layers


Step 6: Local Testing Simulation


Step 7: SAM/CDK Configuration


Step 8: Capstone — Lambda Simulation

Run verification:

📸 Verified Output:


Summary

Concept
Key Point
Implementation

Handler structure

async (event, context) => response

Return HTTP response object

Cold start

Code outside handler runs once

Initialize DB/cache globally

Event sources

Different event shapes

API GW, SQS, S3, DynamoDB Streams

Middy middleware

Onion model

Auth, validation, error handling

Lambda Layers

Shared deps at /opt/nodejs

Reduce bundle size

Local testing

Mock event/context

Test without deploying

Provisioned concurrency

Pre-warm containers

Eliminate cold starts

Bundle optimization

esbuild/rollup

Smaller = faster cold start

Last updated