Serverless Composer Plugin
A comprehensive Serverless Framework plugin that dynamically composes Serverless Framework services by loading configuration fragments from structured directories. Now includes module-scoped deployment functionality for deploying specific modules within your Serverless application.
Features
Core Composer Features
- ✅ Dynamic configuration loading: Load functions, resources, outputs, etc. from structured directories
- ✅ Cross-version compatibility: Works with Serverless Framework v2, v3 & v4
- ✅ Per-category transformers: Optional transformation functions for each category
- ✅ Variable resolution: Cross-version variable resolution before merging
- ✅ Multiple file formats: Supports YAML, JSON, and JavaScript/TypeScript files
Module-Scoped Deployment Features
- ✅ Module-scoped deployments: Deploy only specific modules instead of the entire service
- ✅ Complete isolation: Module deployments are completely isolated from other modules
- ✅ Configurable module directory: Customize where modules are located
- ✅ CLI command integration: Simple
deployModule command with shorthand options
- ✅ Automatic stack naming: Follows
{service}-{module}-{stage} pattern
Installation
Production Install
npm install @hyperdrive.bot/serverless-composer-plugin
npm install @hyperdrive.bot/serverless-composer-plugin@alpha
Add the plugin to your serverless.yml file:
plugins:
- '@hyperdrive.bot/serverless-composer-plugin'
Development Install (Local)
cd packages/serverless/composer
npm run build
Configuration
Configure the plugin in your serverless.yml:
custom:
composer:
modulesDir: serverless/modules
functions:
directory: custom/functions
transformer: path/to/functions-transformer.js
resources:
directory: custom/resources
transformer: path/to/resources-transformer.js
stepFunctions:
directory: custom/stepFunctions
transformer: path/to/stepfunctions-transformer.js
Directory Structure
Regular Composer Structure
For regular deployments, organize your configuration files under serverless/:
project/
├── serverless.yml
├── serverless/
│ ├── functions/
│ │ ├── users.yml
│ │ ├── orders.yml
│ │ └── notifications.yml
│ ├── resources/
│ │ ├── dynamodb.yml
│ │ ├── s3.yml
│ │ └── api-gateway.yml
│ ├── stepFunctions/
│ │ └── order-processing.yml
│ └── outputs/
│ └── api-endpoints.yml
Module-Based Structure
For module-scoped deployments, organize modules under the configured modulesDir:
project/
├── serverless.yml
├── serverless/
│ └── modules/
│ ├── auth/
│ │ ├── functions/
│ │ │ ├── login.yml
│ │ │ └── register.yml
│ │ ├── resources/
│ │ │ └── user-table.yml
│ │ └── outputs/
│ │ └── auth-outputs.yml
│ ├── payment/
│ │ ├── functions/
│ │ │ ├── process-payment.yml
│ │ │ └── refund.yml
│ │ └── resources/
│ │ └── payment-queue.yml
│ └── notifications/
│ ├── functions/
│ │ └── send-notification.yml
│ └── stepFunctions/
│ └── notification-workflow.yml
Usage
Regular Deployment
For regular deployments that compose all configuration fragments:
serverless deploy
This will load and merge all configuration files from the structured directories.
Module-Scoped Deployment
Deploy only the auth module:
serverless deployModule --module auth
Or using the shorthand:
serverless deployModule -m auth
What happens during module deployment:
- Stack name set to
{originalService}-{module}-{stage} (e.g., my-service-auth-dev)
- Complete isolation - clears existing config to prevent interference
- Composes only the configuration from
serverless/modules/auth/
- Automatically triggers
serverless deploy to deploy the composed module
- Shows progress logs with
[composer] prefix
Module-Scoped Removal
Remove only the auth module:
serverless removeModule --module auth
Or using the shorthand:
serverless removeModule -m auth
What happens during module removal:
- Stack name set to
{originalService}-{module}-{stage} (e.g., my-service-auth-dev)
- Complete isolation - clears existing config to prevent interference
- Composes only the configuration from
serverless/modules/auth/
- Automatically triggers
serverless remove to delete the composed module
- Shows progress logs with
[composer] prefix
Deploy Multiple Modules
To deploy multiple modules, run the command for each module:
serverless deployModule -m auth
serverless deployModule -m payment
serverless deployModule -m notifications
Remove Multiple Modules
To remove multiple modules, run the command for each module:
serverless removeModule -m auth
serverless removeModule -m payment
serverless removeModule -m notifications
Supported Categories
The plugin supports the following configuration categories:
functions: Lambda function definitions
resources: CloudFormation resources
stepFunctions: Step Functions state machines
lambdaRoleStatements: IAM role statements for Lambda functions
outputs: CloudFormation outputs
userRoleStatements: Custom user role statements
modules: Module-specific configurations
Each category can be customized with:
directory: Custom path for the category files (defaults to serverless/{categoryName})
transformer: Optional function to transform loaded data before merging
File Format Support
The plugin supports multiple file formats in each category directory:
- YAML:
.yml, .yaml
- JSON:
.json
- JavaScript:
.js, .cjs, .mjs
- TypeScript:
.ts
Example Function Definition
getUser:
handler: src/handlers/users.getUser
events:
- http:
path: /users/{id}
method: get
createUser:
handler: src/handlers/users.createUser
events:
- http:
path: /users
method: post
Example Resource Definition
UserTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: ${self:service}-users-${opt:stage}
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: userId
AttributeType: S
KeySchema:
- AttributeName: userId
KeyType: HASH
Advanced Configuration
Custom Module Directory
custom:
composer:
modulesDir: my-custom-modules-path
Custom Category Directories
You can customize the directory path for each category:
custom:
composer:
functions:
directory: my-custom-functions-dir
resources:
directory: my-custom-resources-dir
stepFunctions:
directory: my-custom-stepfunctions-dir
outputs:
directory: my-custom-outputs-dir
Category-Specific Transformers
Transformers allow you to modify configuration fragments before they're merged:
custom:
composer:
functions:
directory: custom/functions
transformer: ./transformers/functions.js
resources:
directory: custom/resources
transformer: ./transformers/resources.js
Transformer Example
module.exports = (payload, serverless) => {
const functionsWithEnv = {}
for (const [name, config] of Object.entries(payload)) {
functionsWithEnv[name] = {
...config,
environment: {
...config.environment,
STAGE: serverless.service.provider.stage,
SERVICE_NAME: serverless.service.service
}
}
}
return functionsWithEnv
}
TypeScript Transformer Example
import { ServerlessInstance } from 'serverless'
interface ResourceConfig {
[key: string]: any
}
export default (payload: ResourceConfig, serverless: ServerlessInstance): ResourceConfig => {
const resourcesWithTags = {}
for (const [name, config] of Object.entries(payload)) {
if (config.Type && config.Properties) {
resourcesWithTags[name] = {
...config,
Properties: {
...config.Properties,
Tags: [
...(config.Properties.Tags || []),
{ Key: 'Service', Value: serverless.service.service },
{ Key: 'Stage', Value: serverless.service.provider.stage }
]
}
}
} else {
resourcesWithTags[name] = config
}
}
return resourcesWithTags
}
How It Works
Regular Composer Mode
- Plugin initialization: Detects Serverless Framework version
- Directory scanning: Scans configured directories for files
- File loading: Loads and parses YAML, JSON, and JavaScript files
- Content merging: Deep merges all content within each category
- Transformation: Applies optional transformers
- Variable resolution: Resolves Serverless variables (v2 only)
- Service composition: Merges categories into the service configuration
Module-Scoped Mode
When you run deployModule --module myModule:
- Stack naming: Sets stack name to
{service}-{module}-{stage}
- Configuration clearing: Removes existing functions, resources, etc. for isolation
- Directory scoping: Redirects all category paths to
{modulesDir}/{module}/
- Module processing: Loads only configuration from the specified module
- Composition: Merges module-specific configuration into service
- Deployment: Automatically triggers
serverless deploy
Version Compatibility
- Serverless v2: Uses hooks with
populateObject for variable resolution
- Serverless v3+: Uses constructor-time processing with
extendConfiguration
The plugin automatically detects your Serverless Framework version and uses the appropriate integration method.
Troubleshooting
Module Directory Not Found
Error: Module directory not found: /path/to/serverless/modules/myModule
Solution: Ensure the module directory exists and contains the expected category subdirectories.
Module Directory Not Found (Remove Command)
Warning: Module directory not found: /path/to/serverless/modules/myModule
Proceeding with removal in case resources still exist in AWS...
Note: This is expected behavior for removeModule. The command will proceed with removal even if the local module directory doesn't exist, as the AWS resources may still need to be cleaned up.
Transformer Errors
Transformer error in 'functions': Cannot read property 'stage' of undefined
Solution: Check your transformer function - ensure it handles the serverless instance correctly and doesn't assume properties exist.
Variable Resolution Issues (v2)
If variables aren't resolving correctly, ensure:
- Variables are valid Serverless Framework syntax
- Referenced resources exist in the service configuration
- The plugin is loaded before other plugins that might conflict
Remove Command Not Triggering
⚠️ Cannot trigger automatic removal. Please run 'serverless remove' manually.
Solution: The plugin couldn't access the Serverless plugin manager. Run serverless remove manually to complete the module removal.
Migration from Module-Composer
If you were previously using serverless-module-composer-plugin:
-
Update plugin reference:
plugins:
- serverless-composer-plugin
-
Update configuration:
custom:
composer:
modulesDir: serverless/modules
-
Commands work identically:
serverless deployModule -m myModule
serverless removeModule -m myModule
-
All functionality preserved: No breaking changes to existing workflows
Development
Building the Plugin
npm run build
Running Tests
npm test
Linting
npm run lint
npm run lint:fix
License
MIT