Serverless Package

The @hazeljs/serverless package provides seamless deployment of HazelJS applications to serverless platforms including AWS Lambda, Google Cloud Functions, and Azure Functions. It includes adapters, cold start optimization, and automatic request/response transformation.

Purpose

Deploying applications to serverless platforms requires adapting your code to platform-specific event formats, handling cold starts, and managing function configurations. The @hazeljs/serverless package simplifies serverless deployment by providing:

  • Platform Adapters: Unified interface for AWS Lambda, Google Cloud Functions, and Azure Functions
  • Cold Start Optimization: Built-in optimizations to reduce cold start times
  • Automatic Transformation: Converts platform events to HTTP requests automatically
  • Decorator-Based: Mark controllers as serverless-optimized with decorators
  • Configuration Management: Easy configuration for memory, timeout, and other settings

Architecture

The package uses adapter pattern to abstract platform differences:

Loading diagram...

Key Components

  1. Platform Adapters: Convert platform events to HTTP requests
  2. Cold Start Optimizer: Pre-warms application to reduce latency
  3. @Serverless Decorator: Marks controllers for serverless optimization
  4. Handler Factory: Creates platform-specific handlers

Advantages

1. Platform Agnostic

Write once, deploy to multiple serverless platforms without code changes.

2. Cold Start Optimization

Built-in optimizations reduce cold start times, improving user experience.

3. Automatic Transformation

No need to manually convert platform events—handled automatically.

4. Developer Experience

Use the same HazelJS code for both traditional and serverless deployments.

5. Cost Optimization

Configuration options help optimize memory and timeout settings for cost efficiency.

6. Production Ready

Includes error handling, logging, and proper response formatting for all platforms.

Installation

npm install @hazeljs/serverless

Quick Start

AWS Lambda

Create a Lambda handler:

// handler.ts
import { createLambdaHandler } from '@hazeljs/serverless';
import { AppModule } from './app.module';

export const handler = createLambdaHandler(AppModule);

Google Cloud Function

Create a Cloud Function handler:

// handler.ts
import { createCloudFunctionHandler } from '@hazeljs/serverless';
import { AppModule } from './app.module';

export const handler = createCloudFunctionHandler(AppModule);

@Serverless Decorator

The @Serverless decorator is a class decorator that marks a controller or module as optimized for serverless deployment. It configures serverless-specific settings and enables optimizations.

Understanding @Serverless Decorator

Purpose: Configures your application for optimal serverless deployment. It sets memory, timeout, and optimization settings that are used when deploying to platforms like AWS Lambda or Google Cloud Functions.

How it works:

  1. Metadata Storage: Stores serverless configuration in class metadata
  2. Deployment Configuration: Used by deployment tools to set function settings
  3. Cold Start Optimization: Enables optimizations to reduce cold start times
  4. Platform Adaptation: Helps adapters configure platform-specific settings

Configuration Options:

interface ServerlessOptions {
  memory?: number;              // Memory allocation in MB (default: 512)
  timeout?: number;            // Timeout in seconds (default: 30)
  coldStartOptimization?: boolean; // Enable cold start optimizations (default: true)
  environment?: Record<string, string>; // Environment variables
  runtime?: string;            // Runtime platform: 'aws-lambda', 'gcp-functions', etc.
  autoSplit?: boolean;         // Automatic function splitting (default: false)
  reservedConcurrency?: number; // AWS Lambda reserved concurrency
  provisionedConcurrency?: number; // AWS Lambda provisioned concurrency
}

Example with Detailed Explanation:

import { Controller, Get } from '@hazeljs/core';
import { Serverless } from '@hazeljs/serverless';

// @Serverless is a class decorator that configures serverless deployment
@Serverless({
  memory: 512,                    // Allocate 512 MB of memory
  // More memory = faster execution but higher cost
  // AWS Lambda: 128 MB to 10,240 MB
  // Google Cloud Functions: 128 MB to 8,192 MB
  
  timeout: 30,                    // Function timeout: 30 seconds
  // Maximum: AWS Lambda (900s), GCP Functions (540s)
  // Set based on your function's expected execution time
  
  coldStartOptimization: true,    // Enable cold start optimizations
  // Pre-warms the application
  // Caches dependencies
  // Optimizes module loading
  // Reduces first request latency
  
  environment: {
    NODE_ENV: 'production',
    LOG_LEVEL: 'info',
  },
  // Environment variables available to the function
  
  runtime: 'aws-lambda',          // Target platform
  // Options: 'aws-lambda', 'gcp-functions', 'azure-functions'
})
@Controller('/api')
export class ApiController {
  @Get()
  async handler() {
    // This controller is optimized for serverless deployment
    // The @Serverless decorator ensures:
    // - Proper memory allocation
    // - Appropriate timeout settings
    // - Cold start optimizations are applied
    // - Platform-specific configurations are set
    return { message: 'Hello from serverless!' };
  }
}

Memory Configuration:

Memory allocation affects both performance and cost:

@Serverless({
  memory: 1024,  // 1 GB
  // Higher memory = more CPU power
  // Faster execution = lower duration costs
  // But higher per-request cost
  // Find the sweet spot for your workload
})

Cold Start Optimization:

Cold starts occur when a function hasn't been used recently. The decorator enables optimizations:

@Serverless({
  coldStartOptimization: true,
  // This enables:
  // 1. Pre-warming: Initializes app before first request
  // 2. Dependency caching: Caches loaded modules
  // 3. Lazy loading: Only loads what's needed
  // 4. Connection pooling: Reuses database connections
})

Platform-Specific Settings:

// AWS Lambda specific
@Serverless({
  memory: 512,
  timeout: 30,
  reservedConcurrency: 10,      // AWS: Reserve 10 concurrent executions
  provisionedConcurrency: 5,   // AWS: Keep 5 instances warm
  // Provisioned concurrency eliminates cold starts
  // But costs more (you pay for idle instances)
})

// Google Cloud Functions
@Serverless({
  memory: 512,
  timeout: 60,                  // GCP allows up to 540 seconds
  runtime: 'gcp-functions',
})

Best Practices:

  1. Right-size memory: Start with 512MB, monitor, and adjust
  2. Set appropriate timeouts: Based on your function's needs
  3. Enable cold start optimization: Especially for user-facing functions
  4. Use provisioned concurrency: For critical, low-latency functions
  5. Monitor costs: Balance memory, timeout, and concurrency settings
  6. Test locally: Use serverless emulators to test before deployment

Lambda Adapter

Use the Lambda adapter for AWS Lambda deployment:

import { LambdaAdapter } from '@hazeljs/serverless';
import { AppModule } from './app.module';

const adapter = new LambdaAdapter(AppModule);
export const handler = adapter.createHandler();

Lambda Handler Example

import { createLambdaHandler, LambdaEvent, LambdaContext } from '@hazeljs/serverless';
import { AppModule } from './app.module';

export const handler = createLambdaHandler(AppModule);

// The handler automatically:
// - Initializes your HazelJS application
// - Converts Lambda events to HTTP requests
// - Processes requests through your controllers
// - Returns properly formatted Lambda responses

Cloud Function Adapter

Use the Cloud Function adapter for Google Cloud Functions:

import { CloudFunctionAdapter } from '@hazeljs/serverless';
import { AppModule } from './app.module';

const adapter = new CloudFunctionAdapter(AppModule);
export const handler = adapter.createHandler();

Cold Start Optimization

Enable cold start optimization to improve performance:

@Serverless({
  coldStartOptimization: true,
  memory: 1024, // More memory = faster cold starts
})
@Controller('/api')
export class ApiController {
  // Your controller
}

The cold start optimizer:

  • Pre-warms the application
  • Caches dependencies
  • Optimizes module loading

Serverless Options

Configure serverless deployment:

@Serverless({
  memory: 512,              // Memory allocation in MB
  timeout: 30,              // Timeout in seconds
  coldStartOptimization: true,
  environment: {
    NODE_ENV: 'production',
  },
  runtime: 'aws-lambda',   // or 'gcp-functions', 'azure-functions'
  autoSplit: false,         // Automatic function splitting
  reservedConcurrency: 10,  // AWS Lambda reserved concurrency
  provisionedConcurrency: 5, // AWS Lambda provisioned concurrency
})

Complete Example

Application Module

import { HazelModule } from '@hazeljs/core';
import { Serverless } from '@hazeljs/serverless';
import { Controller, Get, Post, Body } from '@hazeljs/core';

@Serverless({
  memory: 512,
  timeout: 30,
  coldStartOptimization: true,
})
@HazelModule({
  controllers: [ApiController],
})
export class AppModule {}

Controller

import { Controller, Get, Post, Body } from '@hazeljs/core';

@Controller('/api')
export class ApiController {
  @Get('/health')
  async health() {
    return { status: 'ok' };
  }

  @Post('/users')
  async createUser(@Body() body: { name: string; email: string }) {
    return {
      id: 1,
      ...body,
    };
  }
}

Lambda Handler

// handler.ts
import { createLambdaHandler } from '@hazeljs/serverless';
import { AppModule } from './app.module';

export const handler = createLambdaHandler(AppModule);

Deployment Configuration

AWS Lambda (serverless.yml)

service: my-hazeljs-app

provider:
  name: aws
  runtime: nodejs18.x
  memorySize: 512
  timeout: 30

functions:
  api:
    handler: handler.handler
    events:
      - http:
          path: /{proxy+}
          method: ANY
      - http:
          path: /
          method: ANY

Google Cloud Functions

// Deploy with gcloud CLI
// gcloud functions deploy myFunction --runtime nodejs18 --trigger http

Request/Response Transformation

The adapters automatically transform:

  • Lambda Event → HTTP Request
  • HTTP Response → Lambda Response
// Lambda event is automatically converted to:
{
  method: 'POST',
  url: '/api/users',
  headers: { 'content-type': 'application/json' },
  query: {},
  params: {},
  body: { name: 'John', email: 'john@example.com' },
  context: {
    requestId: 'abc123',
    functionName: 'my-function',
    remainingTime: 29000,
  },
}

Error Handling

Errors are automatically handled and returned as proper HTTP responses:

@Controller('/api')
export class ApiController {
  @Get('/error')
  async error() {
    throw new Error('Something went wrong');
    // Automatically returns:
    // {
    //   statusCode: 500,
    //   body: JSON.stringify({ error: 'Internal Server Error' })
    // }
  }
}

Best Practices

  1. Optimize cold starts: Use cold start optimization and appropriate memory allocation.

  2. Keep functions small: Split large applications into multiple functions.

  3. Use environment variables: Store configuration in environment variables, not code.

  4. Monitor performance: Track cold start times and optimize accordingly.

  5. Handle timeouts: Set appropriate timeout values based on your function's needs.

  6. Use provisioned concurrency: For critical functions, use provisioned concurrency to eliminate cold starts.

What's Next?

  • Learn about Config for serverless configuration
  • Explore Cache for serverless caching
  • Check out WebSocket for real-time serverless