Custom Initialization

12003

During the application startup, we often need to perform some initialization work. Only after the initialization is complete can the application start successfully and begin providing services.

The framework provides a unified entry file (app.js) for customizing the startup process. This file needs to return a Boot class. We can execute the initialization work during the application startup process by defining lifecycle methods within the Boot class.

The framework provides the following lifecycle functions for developers to handle:

  • Configuration file is about to load, which is the last opportunity to dynamically modify the configuration (configWillLoad);
  • Configuration file loading is complete (configDidLoad);
  • File loading is complete (didLoad);
  • Plugin startup is complete (willReady);
  • Worker is ready (didReady);
  • Application startup is complete (serverDidReady);
  • Application is about to close (beforeClose).

We can define this Boot class in app.js. Below, we extract several commonly used lifecycle functions in application development as examples:

// app.js
class AppBootHook {
  constructor(app) {
    this.app = app;
  }

  configWillLoad() {
    // At this point, the config file has been read and merged, but has not yet taken effect
    // This is the last chance for the application layer to modify the configuration
    // Note: This function only supports synchronous calls

    // For example: the password in the parameters is encrypted, decrypt it here
    this.app.config.mysql.password = decrypt(this.app.config.mysql.password);
    // For example: insert a middleware between the framework's coreMiddleware
    const statusIdx = this.app.config.coreMiddleware.indexOf('status');
    this.app.config.coreMiddleware.splice(statusIdx + 1, 0, 'limit');
  }

  async didLoad() {
    // All configurations have been loaded
    // Can be used to load custom application files and start custom services

    // For example: create an instance of the custom application
    this.app.queue = new Queue(this.app.config.queue);
    await this.app.queue.init();

    // For example: load a custom directory
    this.app.loader.loadToContext(path.join(__dirname, 'app/tasks'), 'tasks', {
      fieldClass: 'tasksClasses',
    });
  }

  async willReady() {
    // All plugins have started, but the application as a whole is not yet ready
    // Data initialization and other operations can be performed, and only after these operations succeed will the application start

    // For example: load data from the database into memory cache
    this.app.cacheData = await this.app.model.query(QUERY_CACHE_SQL);
  }

  async didReady() {
    // The application has started

    const ctx = await this.app.createAnonymousContext();
    await ctx.service.Biz.request();
  }

  async serverDidReady() {
    // The http/https server has started and is ready to receive external requests
    // At this point, the server instance can be obtained from app.server

    this.app.server.on('timeout', socket => {
      // Handle socket timeout
    });
  }
}

module.exports = AppBootHook;

Note: It is not recommended to perform time-consuming operations in custom lifecycle functions, as the framework will have a startup timeout detection.

If your Egg framework's lifecycle functions are of an older version, it is recommended to upgrade them to class method mode; for details, please refer to Upgrade Your Lifecycle Event Functions.