DEV Community

Cover image for Unlocking Egg.js: From Node.js Newbie to Web Development Pro
Matty
Matty

Posted on • Edited on

Unlocking Egg.js: From Node.js Newbie to Web Development Pro

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.

Image description

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;
Enter fullscreen mode Exit fullscreen mode

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: '[email protected]' };
}
}
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 = () => {
return async (ctx, next) => {
console.log(
[${new Date().toISOString()}] ${ctx.method} ${ctx.url});
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)

Collapse
 
bellaaacoder profile image
Taoling Shen

useful info provided☺️

Collapse
 
mattyedwards profile image
Matty

Then I'm relieved.

Collapse
 
andy124 profile image
synncb

Great! I'm also using servbay for environment construction.It's really convenient to switch between multiple environments at will!😃

Collapse
 
mattyedwards profile image
Matty

Yes, servbay is really a great development tool.

Some comments may only be visible to logged-in visitors. Sign in to view all comments.

OSZAR »