Introduction to Egg.js: MVC Architecture, Routing, Controllers, Services, and Configuration

The article introduces Egg.js, a Node.js framework that implements the classic MVC pattern with clearly separated controllers, services, and views, explains routing, configuration, project layout, multi‑process architecture, scheduled tasks, and plugin system, and demonstrates building a Hacker News clone.

NetEase Cloud Music Tech Team
NetEase Cloud Music Tech Team
NetEase Cloud Music Tech Team
Introduction to Egg.js: MVC Architecture, Routing, Controllers, Services, and Configuration

Egg.js is a Node.js backend framework that adopts the classic MVC (Model‑View‑Controller) design pattern to build web applications such as a Hacker News clone.

Getting Started

Before reading the tutorial, it is recommended to review the official Egg.js example and become familiar with Koa.js.

MVC Overview

The MVC pattern in Egg.js consists of:

Model (Modal) : Handles data and business logic, usually split into service (business) and dao (database) layers.

View : Responsible for page layout and rendering.

Controller : Routes incoming requests to the appropriate Model and View.

For illustration, the Java Spring MVC example is shown below:

@Controller
public class GreetingController {

  @GetMapping("/greeting")
  public String greeting(@RequestParam(name="ownerId", required=false, defaultValue="World") String ownerId, Model model) {
    String name = ownerService.findOwner(ownerId);
    model.addAttribute("name", name);
    return "greeting";
  }
}

The corresponding template greeting.html uses a simple placeholder to display the name.

Egg.js Implementation

Egg.js follows the same MVC concepts. The project structure typically includes app/controller, app/service, and app/view directories.

Controller Example

// app/controller/home.js
const Controller = require('egg').Controller;

class HomeController extends Controller {
  async index() {
    this.ctx.body = 'Hello world';
  }
}
module.exports = HomeController;

This controller defines an index method that returns a simple string.

Routing

// app/router.js
module.exports = app => {
  const { router, controller } = app;
  router.get('/', controller.home.index);
};

The router maps the root URL / to controller.home.index.

News List Controller

// app/controller/news.js
const Controller = require('egg').Controller;

class NewsController extends Controller {
  async list() {
    const ctx = this.ctx;
    const page = ctx.query.page || 1;
    const newsList = await ctx.service.news.list(page);
    await ctx.render('news/list.tpl', { list: newsList });
  }
}
module.exports = NewsController;

The list action obtains the current page number, calls the news service, and renders the news/list.tpl view.

Service Example

// app/service/news.js
const Service = require('egg').Service;

class NewsService extends Service {
  async list(page = 1) {
    const { serverUrl, pageSize } = this.config.news;
    const { data: idList } = await this.ctx.curl(`${serverUrl}/topstories.json`, {
      dataType: 'json',
    });
    const newsList = await Promise.all(Object.keys(idList).map(key => {
      const url = `${serverUrl}/item/${idList[key]}.json`;
      return this.ctx.curl(url, { dataType: 'json' });
    }));
    return newsList.map(res => res.data);
  }
}
module.exports = NewsService;

The service fetches the list of story IDs from Hacker News, then retrieves each story's details in parallel.

Configuration

// config/config.default.js
exports.news = {
  pageSize: 5,
  serverUrl: 'https://hacker-news.firebaseio.com/v0',
};

Configuration items such as pageSize and serverUrl are placed in config.default.js, making them easy to adjust per environment.

View Template (Nunjucks)

<!-- app/view/news/list.tpl -->
<html>
  <head>
    <title>Hacker News</title>
    <link rel="stylesheet" href="/public/css/news.css" />
  </head>
  <body>
    <ul class="news-view view">
      {% for item in list %}
        <li class="item"><a href="{{ item.url }}">{{ item.title }}</a></li>
      {% endfor %}
    </ul>
  </body>
</html>

The template iterates over the list variable and renders each news item.

Project Directory Structure

egg-project
├── package.json
├── app.js (optional)
├── agent.js (optional)
├── app
│   ├── router.js
│   ├── controller
│   │   └── home.js
│   ├── service (optional)
│   │   └── user.js
│   ├── middleware (optional)
│   │   └── response_time.js
│   ├── schedule (optional)
│   │   └── my_task.js
│   ├── public (optional)
│   │   └── reset.css
│   ├── view (optional)
│   │   └── home.tpl
│   └── extend (optional)
│       ├── helper.js
│       ├── request.js
│       ├── response.js
│       ├── context.js
│       ├── application.js
│       └── agent.js
├── config
│   ├── plugin.js
│   ├── config.default.js
│   ├── config.prod.js
│   ├── config.test.js (optional)
│   ├── config.local.js (optional)
│   └── config.unittest.js (optional)
└── test
    ├── middleware
    │   └── response_time.test.js
    └── controller
        └── home.test.js

This hierarchy separates configuration, business logic, and static resources, making the application maintainable.

Multi‑Process Model

Egg.js runs three types of processes:

Master : One instance, manages other processes.

Agent : One instance, handles background tasks such as long‑living connections.

Worker : Multiple instances (default equals CPU cores), executes the actual application code.

For example, on a MacBook Pro with 8 cores, Egg.js will start 8 workers, 1 agent, and 1 master.

Schedule Tasks

// app/schedule/force_refresh.js
exports.schedule = {
  interval: '10m',
  type: 'all', // all workers execute
};
exports.schedule = {
  interval: '10s',
  type: 'worker', // only one random worker executes
};

Schedules can be defined to run either on all workers or on a single worker.

Plugin System

Egg.js plugins can have dependencies, environment constraints, and enable/disable switches. They are defined in config/plugin.js and can be loaded from npm packages or local directories.

// package.json snippet for a plugin
{
  "private": true,
  "dependencies": {
    "autoprefixer": "^6.5.4"
  },
  "browserslist": [
    "last 1 version",
    "> 1%",
    "IE 10"
  ]
}

Plugins are merged in the order: plugin config → framework config → application config.

Conclusion

This guide provides a comprehensive introduction to Egg.js, covering MVC architecture, routing, controllers, services, view rendering, configuration management, multi‑process operation, scheduling, and plugin design. It serves as a solid foundation for developers who want to build robust backend applications with Egg.js.

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.

MVCConfigurationNode.jsroutingWeb frameworkegg.js
NetEase Cloud Music Tech Team
Written by

NetEase Cloud Music Tech Team

Official account of NetEase Cloud Music Tech Team

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.