Welcome to our comprehensive guide on building structured web applications using Express.js!
Whether you’re a beginner or an experienced developer, this post will walk you through creating a well-organized Express.js application with best practices in mind.
By the end of this guide, you’ll have a solid understanding of how to structure your code. You will also know how to implement routing and handle errors. There is more to learn as well.
Let’s dive in!
Table of Contents
- Introduction to Express.js
- Setting Up Your Project
- Structuring Your Application
- Implementing Routes and Controllers
- Handling Errors Gracefully
- Using Environment Variables
- Conclusion
1. Introduction to Express.js
Express.js is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications.
It’s known for its simplicity and ease of use, making it a popular choice among developers.
2. Setting Up Your Project
To get started, you’ll need to set up your project. First, create a new directory for your application and navigate into it:
mkdir my-express-app
cd my-express-app
Next, initialize a new Node.js project and install the necessary dependencies:
npm init -y
npm install express body-parser dotenv
Create a .env
file to store your environment variables:
PORT=3000
Now, let’s create the main application file, app.js
:
const express = require('express');
const bodyParser = require('body-parser');
const indexRouter = require('./routes/index');
const usersRouter = require('./routes/users');
const errorHandler = require('./middleware/errorHandler');
require('dotenv').config();
const app = express();
// Middleware
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// Routes
app.use('/', indexRouter);
app.use('/users', usersRouter);
// Error handling middleware
app.use(errorHandler);
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
3. Structuring Your Application
A well-structured application is easier to maintain and scale. Here’s a suggested project structure:
my-express-app/
├── app.js
├── routes/
│ ├── index.js
│ └── users.js
├── middleware/
│ └── errorHandler.js
├── controllers/
│ └── userController.js
├── models/
│ └── userModel.js
├── package.json
└── .env
4. Implementing Routes and Controllers
Routes define the endpoints of your application, while controllers handle the logic for each route. Let’s create some routes and controllers.
routes/index.js
const express = require('express');
const router = express.Router();
router.get('/', (req, res) => {
res.send('Welcome to the Express.js app!');
});
module.exports = router;
routes/users.js
const express = require('express');
const router = express.Router();
const userController = require('../controllers/userController');
router.get('/', userController.getAllUsers);
router.post('/', userController.createUser);
router.get('/:id', userController.getUserById);
router.put('/:id', userController.updateUser);
router.delete('/:id', userController.deleteUser);
module.exports = router;
controllers/userController.js
const userModel = require('../models/userModel');
exports.getAllUsers = (req, res) => {
const users = userModel.getAllUsers();
res.json(users);
};
exports.createUser = (req, res) => {
const newUser = userModel.createUser(req.body);
res.status(201).json(newUser);
};
exports.getUserById = (req, res) => {
const user = userModel.getUserById(req.params.id);
if (user) {
res.json(user);
} else {
res.status(404).send({ error: 'User not found' });
}
};
exports.updateUser = (req, res) => {
const updatedUser = userModel.updateUser(req.params.id, req.body);
if (updatedUser) {
res.json(updatedUser);
} else {
res.status(404).send({ error: 'User not found' });
}
};
exports.deleteUser = (req, res) => {
const deletedUser = userModel.deleteUser(req.params.id);
if (deletedUser) {
res.json(deletedUser);
} else {
res.status(404).send({ error: 'User not found' });
}
};
5. Handling Errors Gracefully
Error handling is crucial for a robust application.
Let’s create a middleware for error handling.
Read more about errors handling in Express.js: How to Handle 404 Errors in Express.js
middleware/errorHandler.js
const userModel = require('../models/userModel');
exports.getAllUsers = (req, res) => {
const users = userModel.getAllUsers();
res.json(users);
};
exports.createUser = (req, res) => {
const newUser = userModel.createUser(req.body);
res.status(201).json(newUser);
};
exports.getUserById = (req, res) => {
const user = userModel.getUserById(req.params.id);
if (user) {
res.json(user);
} else {
res.status(404).send({ error: 'User not found' });
}
};
exports.updateUser = (req, res) => {
const updatedUser = userModel.updateUser(req.params.id, req.body);
if (updatedUser) {
res.json(updatedUser);
} else {
res.status(404).send({ error: 'User not found' });
}
};
exports.deleteUser = (req, res) => {
const deletedUser = userModel.deleteUser(req.params.id);
if (deletedUser) {
res.json(deletedUser);
} else {
res.status(404).send({ error: 'User not found' });
}
};
6. Using Environment Variables
Environment variables help keep your configuration flexible and secure.
We’ve already set up a .env
file to store the port number.
.env
PORT=3000
Conclusion
Congratulations! You’ve now built a well-structured Express.js application with best practices in mind.
This setup will help you maintain and scale your application as it grows. Happy coding!
What’s the next? Off course, here is How to Set Up JWT Authentication in Express.js