Creating a custom plugin lets you:
- Hook into lifecycle events to add new logic
- Define new CLI commands
- Define new variable sources
- Extend the
serverless.yml
syntax - Write extra information to the CLI output
- Add support for new cloud providers
The simplest way to create a Serverless Framework plugin is to write a JavaScript file:
'use strict';
class MyPlugin {
constructor() {
// The plugin is loaded
}
}
module.exports = MyPlugin;
The plugin can then be loaded in serverless.yml
via a local path:
# serverless.yml
service: app
functions:
# ...
plugins:
- ./my-plugin.js
Plugins can also be published to NPM and later installed in separate projects.
To correctly configure the plugin's NPM package, set the main
property to point to your plugin file in package.json
:
{
"main": "my-plugin.js"
}
It is also a good practice to add serverless
to the peerDependencies
section. That ensures that your plugin runs only with the Serverless Framework versions it supports.
{
...
"peerDependencies": {
"serverless": "^2.60 || 3"
}
}
Once the plugin is published on NPM, follow the documentation on Installing plugins to use the custom plugin.
Lifecycle events are events that fire sequentially during a CLI command.
Additionally, for each event an additional before
and after
event is created. For example:
before:package:package
package:package
after:package:package
before:deploy:deploy
deploy:deploy
after:deploy:deploy
The initialize
event is shared across all CLI commands and runs when the CLI starts.
Plugins can "hook" into existing lifecycle events to add behavior to commands like deploy
, package
, etc. via the hooks
helper:
'use strict';
class MyPlugin {
constructor() {
this.hooks = {
'initialize': () => this.init(),
'before:deploy:deploy': () => this.beforeDeploy(),
'after:deploy:deploy': () => this.afterDeploy(),
};
}
init() {
// Initialization
}
beforeDeploy() {
// Before deploy
}
afterDeploy() {
// After deploy
}
}
module.exports = MyPlugin;
Plugins can also create their own commands (with their own lifecycle events): read the Custom commands documentation.
The serverless
parameter provides access to the service configuration at runtime:
'use strict';
class MyPlugin {
constructor(serverless) {
this.serverless = serverless;
this.hooks = {
initialize: () => this.init(),
};
}
init() {
console.log('Serverless instance: ', this.serverless);
// `serverless.service` contains the (resolved) serverless.yml config
const service = this.serverless.service;
console.log('Provider name: ', service.provider.name);
console.log('Functions: ', service.functions);
}
}
module.exports = MyPlugin;
Note: configuration values are only resolved after plugins are initialized. Do not try to read configuration in the plugin constructor, as variables aren't resolved yet. Read configuration in lifecycle events only.
The options
parameter provides access to the CLI options provided to the command:
class MyPlugin {
constructor(serverless, options) {
// Log if a --verbose option was passed:
console.log(options.verbose);
}
}
Plugins can be provider specific, which means that run only with a specific provider.
Note: Binding a plugin to a provider is optional. Serverless will always consider your plugin if you don't specify a provider
.
To bind to a specific provider, retrieve it and set the this.provider
property in the plugin constructor:
class MyPlugin {
constructor(serverless, options) {
// bind to a specific provider
this.provider = serverless.getProvider('providerX');
// ...
}
}
The plugin will now only be executed when the service's provider matches the given provider.
If you use Node.js v12.22 or later, ESM plugins are also supported.
export default class MyPlugin {
constructor() {
// The plugin is loaded
}
}