Mastering EggJS: From Reusable Code to Scalable Plugins and Custom Frameworks

This article walks through EggJS’s core design principles, demonstrates building a simple GitHub aggregation API, shows how to evolve reusable code into plugins and custom frameworks, and explains progressive development practices for large‑scale enterprise applications, highlighting configuration, plugin management, and framework layering.

Alipay Experience Technology
Alipay Experience Technology
Alipay Experience Technology
Mastering EggJS: From Reusable Code to Scalable Plugins and Custom Frameworks

Mastering EggJS: Design, Plugins, and Custom Frameworks

This article, originally shared by Ant Group frontend engineer Tianzhu, introduces EggJS’s design philosophy and demonstrates how a reusable code segment can continuously evolve during development.

Core concept: Application logic → incubate plugin prototype → independent plugin → built into a custom framework.

Example Application

Initialize the project with the scaffold:

$ npm init egg --type=simple showcase

./showcase
├── app
│   ├── controller (controller)
│   │   └── home.js
│   ├── service (business logic)
│   │   └── github.js
│   └── router.js (routing)
├── config (configuration)
│   ├── config.default.js
│   ├── config.prod.js
│   └── plugin.js
├── test
├── README.md
└── package.json

Router and Controller

Controller logic:

const { Controller } = require('egg');

class HomeController extends Controller {
  async listReposByOrg() {
    // get query param
    const org = this.ctx.query.org || 'eggjs';
    // call service
    const result = await this.ctx.service.github.listReposByOrg(org);
    // render response
    this.ctx.body = result;
  }
}

module.exports = HomeController;

Router mapping:

module.exports = app => {
  // register route
  app.router.get('/api/repos', app.controller.home.listReposByOrg);
};

Service Logic

const { Service } = require('egg');

class GithubService extends Service {
  async listReposByOrg(org) {
    const { ctx, config } = this;
    const { endpoint, pageCount } = config.github;
    const repos = await ctx.curl(`${endpoint}/orgs/${org}/repos`, {
      data: { per_page: pageCount },
      dataType: 'json',
    });
    if (repos.status !== 200) return [];
    return repos.data.map(repo => repo.name);
  }
}

module.exports = GithubService;

Configuration

// config/config.default.js
exports.github = {
  endpoint: 'https://api.github.com',
  pageCount: 5,
};

// config/config.prod.js
exports.github = {
  pageCount: 50,
};

Running the Application

$ npm run dev

[master] egg started on http://127.0.0.1:7001 (2216ms)

$ curl localhost:7001/api/repos
["eslint-config-egg","egg","egg-bin","egg-mock","egg-logger"]

$ curl localhost:7001/api/repos?org=vuejs
["vue","vue-router","vuejs.org","vue-touch","Discussion"]

Plugins

Egg plugins are a core capability that can encapsulate functionality beyond request middleware, such as scheduled tasks, initialization, or application‑wide services.

Install a view plugin: $ npm i --save egg-view-nunjucks Configure the plugin and view engine:

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

// config/config.default.js
exports.view = {
  defaultViewEngine: 'nunjucks',
  mapping: {
    '.tpl': 'nunjucks',
  },
};

Update the controller to render a template:

class HomeController extends Controller {
  async listReposByOrg() {
    const org = this.ctx.query.org || 'eggjs';
    const result = await this.ctx.service.github.listReposByOrg(org);
    await this.ctx.render('repos.tpl', { org, result });
  }
}

Template (app/view/repos.tpl):

<h1>{{ org }}</h1>
<ul>
{% for item in result %}
  <li>{{ item }}</li>
{% endfor %}
</ul>

Progressive Development – Plugin Evolution

EggJS supports a progressive evolution pattern:

Reusable code → In‑app plugin prototype → Independent plugin → Integrated into custom framework

Example: move the GitHub service and its configuration into a plugin directory plugin/egg-github and mount it via plugin configuration:

// config/plugin.js
exports.github = {
  enable: true,
  path: path.join(__dirname, '../plugin/egg-github'),
};

Later the plugin can be published as an npm package and reused by other applications:

$ npm i --save egg-github

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

Custom Frameworks

When multiple plugins become stable, they can be packaged into a higher‑level framework (e.g., yadan) that provides a unified API and conventions for a specific business domain.

Frameworks share the same directory structure as applications and support multi‑level inheritance, allowing application logic to be seamlessly sunk into the framework.

Custom loader example to auto‑load utilities:

// config/config.default.js
exports.customLoader = {
  utils: {
    directory: 'app/utils',
    inject: 'app',
  },
};

Such conventions enable a consistent development experience across teams while allowing differentiated implementations (e.g., different template engines or user models) behind stable APIs like ctx.render() or ctx.user.

Conclusion

EggJS provides a “framework of frameworks” that helps architects build scalable, customizable solutions.

The progressive development flow—from reusable code to plugins and finally to custom frameworks—requires only minimal configuration changes.

This approach supports enterprise‑level multi‑application management, a consistent developer experience, and sustainable maintenance.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Backend Developmentplugin architectureNode.jscustom-frameworkprogressive development
Alipay Experience Technology
Written by

Alipay Experience Technology

Exploring ultimate user experience and best engineering practices

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.