Plugin

12003

The plugin mechanism is a major feature of our framework. It not only ensures that the core of the framework remains sufficiently streamlined, stable, and efficient, but also promotes the reuse of business logic and the formation of an ecosystem. Some may ask:

  • Koa already has a middleware mechanism; why do we need plugins?
  • What is the relationship between middleware, plugins, and applications? What are the differences among them?
  • How do I use a plugin?
  • How do I write a plugin?

Next, we will discuss these questions one by one.

Why Use Plugins

During our use of Koa middleware, we encountered the following issues:

  1. Middleware loading actually has a sequence, but the middleware itself cannot manage this order and must leave it to the user. This is quite unfriendly; if the order is incorrect, the results can vary greatly.
  2. The role of middleware is to intercept user requests and perform some actions before and after, such as authentication, security checks, access logs, etc. However, in reality, some functionalities are unrelated to requests, such as scheduled tasks, message subscriptions, background logic, etc.
  3. Some functionalities involve very complex initialization logic that needs to be completed when the application starts. Clearly, this is also not suitable for implementation within middleware.

In summary, we need a more powerful mechanism to manage and orchestrate those relatively independent business logics.

Relationship Between Middleware, Plugins, and Applications

A plugin is essentially a "mini-application," almost identical to an application (app):

Their relationships are:

  • Applications can directly import Koa's middleware.
  • When encountering the scenarios mentioned in the previous section, applications need to import plugins.
  • Plugins themselves can contain middleware.
  • Multiple plugins can be packaged into a higher-level framework.

Using Plugins

Plugins are typically reused through npm modules:

$ npm i egg-mysql --save

Note: We recommend introducing dependencies using the ^ notation and strongly advise against locking versions.

{
  "dependencies": {
    "egg-mysql": "^3.0.0"
  }
}

Next, you need to declare it in the application's or framework's config/plugin.js:

// config/plugin.js
// Use mysql plugin
exports.mysql = {
  enable: true,
  package: 'egg-mysql',
};

This allows you to directly use the functionality provided by the plugin:

app.mysql.query(sql, values);

Parameter Introduction

Each configuration item in plugin.js supports:

  • {Boolean} enable - Whether to enable this plugin, default is true
  • {String} package - npm module name, used to import the plugin via npm module
  • {String} path - Absolute path of the plugin, mutually exclusive with package configuration
  • {Array} env - Only enabled in specified running environments, will override the plugin's own configuration in package.json

Enabling and Disabling

For built-in plugins in the higher-level framework, applications can use them without configuring package or path, just specify enable:

// For built-in plugins, you can use the following concise way to enable or disable
exports.onerror = false;

Configuring by Environment

Additionally, we support the plugin.{env}.js pattern, which will load plugin configurations based on the running environment.

For example, if you define a plugin egg-dev that is only used in the development environment and want to load it only in the local environment, you can install it in devDependencies.

// npm i egg-dev --save-dev
// package.json
{
  "devDependencies": {
    "egg-dev": "*"
  }
}

Next, declare it in plugin.local.js:

// config/plugin.local.js
exports.dev = {
  enable: true,
  package: 'egg-dev',
};

This way, when executing npm i --production in the production environment, the egg-dev package does not need to be downloaded.

Note:

  • plugin.default.js does not exist
  • Can only be used at the application level, do not use at the framework level.

Package and Path

  • package is the npm way of importing, and it is the most common way.
  • path is for absolute path imports, for example, if an application has extracted a plugin internally but has not yet published it to npm; or if the application has rewritten certain plugins of the framework.
  • For usage scenarios of these two methods, refer to the Progressive Development document.
// config/plugin.js
const path = require('path');
exports.mysql = {
  enable: true,
  path: path.join(__dirname, '../lib/plugin/egg-mysql'),
};

Plugin Configuration

Plugins generally contain their own default configurations. Application developers can override the corresponding configurations in config.default.js:

// config/config.default.js
exports.mysql = {
  client: {
    host: 'mysql.com',
    port: '3306',
    user: 'test_user',
    password: 'test_password',
    database: 'test'
  }
};

For specific merging rules, refer to Configuration.

Plugin List

  • The framework comes with commonly used plugins for enterprise applications by default:
  • More community plugins can be searched on GitHub egg-plugin.

How to Develop a Plugin

Refer to the documentation: Plugin Development.