1. What Is Egg.js?đ§
In todayâs web development landscape, Node.jsâthanks to its event-driven, non-blocking I/O modelâhas proven its unique strengths in building high-performance, scalable network applications. It has won the favor of countless developers by unifying front-end and back-end JavaScript, dramatically boosting development efficiency.
Egg.js, built on top of Koa, brings even more power and convenience to enterprise-grade Node.js web applications. Its modular design lets you break your app into independent modules that you can develop, test, and deploy in isolation. Out of the box, Egg.js ships with a rich set of middlewareârouting, static file serving, error handling, and moreâso you donât have to reinvent the wheel. Flexible configuration, a robust plugin system, and native multi-process support round out its feature set, making Egg.js a rock-solid choice for complex business needs and high-concurrency scenarios.
2. Environment Setup & Project InitializationâŹď¸
A. Installing Node.js & npm with ServBay
Before diving into Egg.js, you need Node.js (the runtime) and npm (the package manager). Instead of manual installs, ServBay provides a graphical, version-managed way to spin up multiple Node.js versions on your machine.
1ď¸âŁLaunch ServBay and click the Node.js service.
2ď¸âŁChoose your desired version (e.g., 16.x or 18.x) and hit Install.
Once done, open a terminal and verify:
node -v
npm -v
If you see version numbers, youâre all set!
B. Installing Egg.js
Egg.js comes with a CLI tool called egg-init that scaffolds new projects. Install it globally:
npm install -g egg-init
C. Creating a New Egg.js Project
Navigate to your workspace and run:
egg-init my-egg-project --type=simple
cd my-egg-project
npm install
That generates a starter template:
my-egg-project
âââ app
â âââ controller
â â âââ home.js
â âââ service
â âââ router.js
âââ config
â âââ config.default.js
â âââ plugin.js
â âââ config.prod.js
âââ test
â âââ app
â â âââ controller
â â â âââ home.test.js
â â âââ service
â âââ middleware
âââ README.md
âââ package.json
app/: Core codeâcontrollers, services, routers
config/: Default and environment-specific settings
test/: Unit and integration tests
package.json: Dependency declarations
đťYouâre ready to start coding!
⸝
3. Egg.js Core Concepts & Basic Usageđ§Š
A. Routing & Controllers
Routing maps URLs to handler functions. In app/router.js:
module.exports = app => {
const { router, controller } = app;
router.get('/', controller.home.index);
};
Then in app/controller/home.js:
const { Controller } = require('egg');
class HomeController extends Controller {
async index() {
this.ctx.body = 'Hello, Egg.js!';
}
}
module.exports = HomeController;
For a POST endpoint:
// app/router.js
router.post('/register', controller.user.register);
// app/controller/user.js
const { Controller } = require('egg');
class UserController extends Controller {
async register() {
const { username, password } = this.ctx.request.body;
// ... save to DB ...
this.ctx.body = { success: true, message: 'Registration successful' };
}
}
module.exports = UserController;
B. Service Layer
Services encapsulate business logic and keep controllers lean:
// app/service/user.js
const { Service } = require('egg');
class UserService extends Service {
async getUserById(id) {
// Replace with real DB call
return { id, name: 'Alice', age: 25, email: 'alice@example.com' };
}
}
module.exports = UserService;
Controller usage:
// app/controller/user.js
class UserController extends Controller {
async info() {
const id = this.ctx.params.id;
const user = await this.ctx.service.user.getUserById(id);
this.ctx.body = user;
}
}
C. Middleware
Middleware can hook into the request lifecycle for logging, authentication, error handling, etc.
Example: A simple logger in app/middleware/log.js:
module.exports = () => {
[${new Date().toISOString()}] ${ctx.method} ${ctx.url}
return async (ctx, next) => {
console.log();
await next();
};
};
Enable it in config/config.default.js:
module.exports = appInfo => {
const config = {};
config.middleware = ['log'];
return config;
};
D.Configuration Files
All your settings live under config/. In config/config.default.js:
module.exports = appInfo => {
const config = {};
config.port = 7001;
config.logger = { level: 'info' };
return config;
};
Use config.local.js, config.prod.js, etc., to override defaults per environment.
4. Practical Case: Building a Simple Blog Systemđ§
A. Requirements
â
Weâll implement:
List ArticlesďźDisplay a list of all articles, including the title, introduction, release time and other information, so as to facilitate users to quickly browse and select articles of interest.
View Article DetailsďźClick on an article in the article list to view the details of the article, including the complete text, author information, comment area, etc.
Create ArticleďźBloggers can create new articles in the background and fill in the title, text, classification and other information of the articles.
Update ArticleďźFor published articles, bloggers can edit and update, and modify the content, title, classification and other information of the articles.
Delete ArticleďźIf an article is no longer needed, the blogger can delete it.
B. Database Design
Using MySQL, create an articles table:
CREATE TABLE articles (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
content TEXT NOT NULL,
author VARCHAR(50) NOT NULL,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
C. Project Setup
egg-init blog-system --type=simple
cd blog-system
npm install
npm install egg-mysql --save
In config/plugin.js:
exports.mysql = {
enable: true,
package: 'egg-mysql'
};
In config/config.default.js:
config.mysql = {
client: {
host: 'localhost',
port: '3306',
user: 'root',
password: '123456',
database: 'blog_db'
},
app: true,
agent: false
};
D. Implementing Features
Routes (app/router.js):
module.exports = app => {
const { router, controller } = app;
router.get('/articles', controller.article.list);
router.get('/articles/:id', controller.article.detail);
router.post('/articles', controller.article.create);
router.put('/articles/:id', controller.article.update);
router.delete('/articles/:id', controller.article.delete);
};
Controller (app/controller/article.js):
const { Controller } = require('egg');
class ArticleController extends Controller {
async list() {
const articles = await this.ctx.service.article.list();
this.ctx.body = articles;
}
async detail() {
const { id } = this.ctx.params;
const result = await this.ctx.service.article.detail(id);
if (result.length) {
this.ctx.body = result[0];
} else {
this.ctx.status = 404;
this.ctx.body = { message: 'Not Found' };
}
}
async create() {
const data = this.ctx.request.body;
const article = await this.ctx.service.article.create(data);
if (article) {
this.ctx.status = 201;
this.ctx.body = article;
} else {
this.ctx.status = 500;
this.ctx.body = { message: 'Creation Failed' };
}
}
async update() {
const { id } = this.ctx.params;
const data = this.ctx.request.body;
const article = await this.ctx.service.article.update(id, data);
if (article) {
this.ctx.body = article;
} else {
this.ctx.status = 500;
this.ctx.body = { message: 'Update Failed' };
}
}
async delete() {
const { id } = this.ctx.params;
const success = await this.ctx.service.article.delete(id);
if (success) {
this.ctx.body = { message: 'Deleted' };
} else {
this.ctx.status = 500;
this.ctx.body = { message: 'Deletion Failed' };
}
}
}
module.exports = ArticleController;
Service (app/service/article.js):
const { Service } = require('egg');
class ArticleService extends Service {
async list() {
return this.app.mysql.query('SELECT * FROM articles');
}
async detail(id) {
return this.app.mysql.query('SELECT * FROM articles WHERE id = ?', [ id ]);
}
async create(article) {
const result = await this.app.mysql.insert('articles', article);
if (result.affectedRows === 1) return { id: result.insertId, ...article };
return null;
}
async update(id, article) {
const result = await this.app.mysql.update('articles', { id, ...article });
if (result.affectedRows === 1) return { id, ...article };
return null;
}
async delete(id) {
const result = await this.app.mysql.delete('articles', { id });
return result.affectedRows === 1;
}
}
module.exports = ArticleService;
E.Templating with EJS
Install and configure:
npm install egg-view-ejs --save
In config/plugin.js:
exports.ejs = {
enable: true,
package: 'egg-view-ejs'
};
In config/config.default.js:
config.view = {
defaultViewEngine: 'ejs',
mapping: { '.html': 'ejs' },
};
Render in controller:
async list() {
const articles = await this.ctx.service.article.list();
await this.ctx.render('article/list.html', { articles });
}
Template app/view/article/list.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Article List</title>
</head>
<body>
<h1>Articles</h1>
<ul>
<% articles.forEach(a => { %>
<li><a href="/articles/<%= a.id %>"><%= a.title %></a>
â <%= a.author %> â <%= a.create_time %>
</li>
<% }) %>
</ul>
</body>
</html>
5. Conclusion & OutlookđĄ
From core concepts to a hands-on blog project, weâve explored Egg.js in depth. Leveraging ServBay for rapid, all in one web environment setup, we focused squarely on coding, experiencing Egg.jsâs modular architecture, built-in middleware, flexible config, and powerful plugins firsthand.
Looking ahead, as Node.js evolves and patterns like microservices and Serverless gain ground, Egg.js is poised for even broader adoption in enterprise contexts. Its thriving ecosystem continues to produce new plugins and tools, enriching our development toolkit.
Happy coding! đ If you try Egg.js + ServBay in your next project, feel free to share your experiences or questionsâIâd love to hear how youâre building amazing apps.
Top comments (4)
Great! I'm also using servbay for environment construction.It's really convenient to switch between multiple environments at will!đ
Yes, servbay is really a great development tool.
useful info providedâşď¸
Then I'm relieved.
Some comments may only be visible to logged-in visitors. Sign in to view all comments.