Part 18 – Serverless Development

Serverless computing allows you to build applications without managing infrastructure. This guide covers developing, deploying, and optimizing Node.js functions in serverless environments.

1. Serverless Fundamentals

Key Characteristics

  • No server management
  • Automatic scaling
  • Pay-per-execution pricing
  • Event-driven execution
  • Stateless by design

Use Cases

  • APIs and microservices
  • File processing pipelines
  • Scheduled tasks
  • Real-time stream processing
  • Chatbots and webhooks

2. AWS Lambda Basics

Basic Function Structure

exports.handler = async (event, context) => {
    try {
        // Process event
        const result = await processEvent(event);

        // Return response
        return {
            statusCode: 200,
            body: JSON.stringify(result),
            headers: {
                'Content-Type': 'application/json'
            }
        };
    } catch (err) {
        console.error('Handler error:', err);
        return {
            statusCode: 500,
            body: JSON.stringify({ error: err.message })
        };
    }
};

Common Trigger Events

  • API Gateway: HTTP requests
  • S3: File uploads
  • DynamoDB: Database changes
  • SQS/SNS: Message queues
  • CloudWatch: Scheduled events

3. Development Tools

Serverless Framework

# serverless.yml
service: my-serverless-app

provider:
  name: aws
  runtime: nodejs14.x
  region: us-east-1

functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: hello
          method: get

# Deploy
serverless deploy

AWS SAM

# template.yaml
Resources:
  HelloFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: hello-world/
      Handler: app.lambdaHandler
      Runtime: nodejs14.x
      Events:
        HelloWorld:
          Type: Api
          Properties:
            Path: /hello
            Method: get

# Build and deploy
sam build
sam deploy --guided

4. Performance Optimization

Cold Start Mitigation

  • Keep functions small (~50MB)
  • Use provisioned concurrency
  • Initialize connections outside handler
  • Use ARM architecture (Graviton2)
  • Avoid monolithic functions

Connection Pooling

const { MongoClient } = require('mongodb');

// Initialize outside handler
let client;
const connect = async () => {
    if (!client) {
        client = new MongoClient(process.env.DB_URI);
        await client.connect();
    }
    return client;
};

exports.handler = async (event) => {
    const db = (await connect()).db();
    // Use db connection
};

5. Advanced Patterns

Function Composition

// Chaining functions with Step Functions
{
  "Comment": "Order processing workflow",
  "StartAt": "ValidateOrder",
  "States": {
    "ValidateOrder": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:...:validateOrder",
      "Next": "ProcessPayment"
    },
    "ProcessPayment": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:...:processPayment",
      "Next": "FulfillOrder"
    }
  }
}

Event Bridge Architecture

// serverless.yml
functions:
  processOrder:
    handler: handlers.processOrder
    events:
      - eventBridge:
          pattern:
            source: ["myapp.orders"]
            detail-type: ["OrderCreated"]

// Emit custom event
const AWS = require('aws-sdk');
const eventBridge = new AWS.EventBridge();

await eventBridge.putEvents({
    Entries: [{
        Source: 'myapp.orders',
        DetailType: 'OrderCreated',
        Detail: JSON.stringify(order),
        EventBusName: 'default'
    }]
}).promise();

6. Testing Strategies

Local Testing

// Using serverless-offline
# Install plugin
npm install serverless-offline --save-dev

# serverless.yml
plugins:
  - serverless-offline

# Run locally
serverless offline start

# Test with curl
curl http://localhost:3000/dev/hello

Mock AWS Services

// Using aws-sdk-mock
const AWS = require('aws-sdk-mock');
const { handler } = require('../handler');

test('processes S3 event', async () => {
    AWS.mock('S3', 'getObject', {
        Body: Buffer.from('test file content')
    });

    const event = { /* mock S3 event */ };
    const result = await handler(event);

    expect(result).toEqual({ processed: true });
    AWS.restore('S3');
});

7. Production Best Practices

Security Considerations

  • Least privilege IAM roles
  • Environment variables for secrets
  • VPC configuration for private resources
  • Regular dependency updates

Monitoring and Debugging

  • CloudWatch Logs and Metrics
  • X-Ray for distributed tracing
  • Custom business metrics
  • Alerting on errors and throttles

Next: Security Best Practices →

Serverless Checklist

  • ✅ Keep functions small and focused
  • ✅ Initialize connections outside handler
  • ✅ Set proper memory and timeout values
  • ✅ Implement proper error handling
  • ✅ Monitor cold start percentage
  • ✅ Secure all environment variables

Leave a Comment

Your email address will not be published. Required fields are marked *