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:
Key Components
- Platform Adapters: Convert platform events to HTTP requests
- Cold Start Optimizer: Pre-warms application to reduce latency
- @Serverless Decorator: Marks controllers for serverless optimization
- 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:
- Metadata Storage: Stores serverless configuration in class metadata
- Deployment Configuration: Used by deployment tools to set function settings
- Cold Start Optimization: Enables optimizations to reduce cold start times
- 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:
- Right-size memory: Start with 512MB, monitor, and adjust
- Set appropriate timeouts: Based on your function's needs
- Enable cold start optimization: Especially for user-facing functions
- Use provisioned concurrency: For critical, low-latency functions
- Monitor costs: Balance memory, timeout, and concurrency settings
- 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
-
Optimize cold starts: Use cold start optimization and appropriate memory allocation.
-
Keep functions small: Split large applications into multiple functions.
-
Use environment variables: Store configuration in environment variables, not code.
-
Monitor performance: Track cold start times and optimize accordingly.
-
Handle timeouts: Set appropriate timeout values based on your function's needs.
-
Use provisioned concurrency: For critical functions, use provisioned concurrency to eliminate cold starts.