Controllers in NodeJS explained with examples
In this guide on ‘Controllers in NodeJS explained with examples’ we’ll be discussing what controllers are and how to use them in your Node.js application with proper examples.
Below is a simple NodeJS code snippet that handles user Registration the native way.
const express = require('express')
const app = express()
// post function for registering user -------------
app.post('/api/register', async(req, res)=>{
const {name, bio, email, password} = req.body;
... Logic to register user
})
// -------------------------------------------------
app.listen(3000, () => {
console.log("listening on port 8000");
});
The registerUser
function can be defined separately from the app.post()
route handler, and then passed as a callback function to app.post()
instead of being defined inside the route handler.
const express = require('express')
const app = express()
// separate function for registering user --------
const registerUser = async (req, res)=>{
const {name, bio, email, password} = req.body;
... Logic to register user
}
// -------------------------------------------------
app.post('/api/register', registerUser); // using the function separately
app.listen(3000, () => {
console.log("listening on port 8000");
});
This approach makes it easier to reuse the same function in other routes or middleware and can help keep the code clean and modular.
Moving to Controllers
Besides writing the function separately we can move the functions to a directory called Controllers
.
A controller is a module that contains the handler functions for a group of related routes. Controllers help separate concerns and make it easier to organize your code. They typically consist of functions that handle requests and responses for a specific resource, such as users, authentication, etc.
To move the registerUser
function to a controller, we can create a new file called authController.js
in the controllers/
directory. Here’s an example of what the updated code would look like:
const registerUser = async (req, res)=>{
const {name, bio, email, password} = req.body;
// Logic to register user
}
const loginUser = async (req, res)=>{
const {email, password} = req.body;
// Logic to login
}
module.exports = {
registerUser,
loginUser,
};
const express = require('express')
const app = express()
// importing the registerUser & loginUser functions
const { registerUser, loginUser } = require('./controllers/authController');
app.use(express.json())
app.post('/api/register', registerUser)
app.post('/api/login', loginUser)
app.listen(3000, () => {
console.log('Server started on port 3000')
})
In app.js
, we import the authController
object using the require
statement and then pass the registerUser
function as a callback to the POST /api/register
route.
In authController.js
, we define the registerUser, loginUser
function and export it using the module.exports
statement.
Related: NodeJS Middlewares explained
To summarize, moving functions to controllers involves creating a new file for the controller module, defining the handler functions within the module, and exporting them using the module.exports
statement. You can then import the controller module into your main application file and use its functions as route handlers.
How to not Confuse
I’ve been getting some requests on ‘How routes are different from controllers’. Here you go:
While both the routes
and controllers
folders can help keep your code clean and organized, they serve different purposes.
image source: https://developer.mozilla.org (opens in a new tab)
This is how you can use Routes and Controllers together, where the request is received by a router, and the router then uses a controller function as a callback.
const express = require('express');
const router = express.Router();
const { registerUser, loginUser } = require('../controllers/authController');
router.post('/register', registerUser);
router.post('/login', loginUser);
module.exports = router;
The routes
folder is where you define the different endpoints of your API and the HTTP methods (e.g., GET, POST, PUT, DELETE) that are supported by those endpoints. Each file in the routes
folder usually corresponds to a specific endpoint, and the code inside the file defines the behavior of that endpoint.
On the other hand, the controllers
folder is where you define the functions that handle the business logic for each endpoint. These functions take in the HTTP request, perform any necessary operations (e.g., accessing a database), and return a response to the client. Each controller file usually corresponds to a specific resource (e.g., users, authentication, etc.) and contains the functions that handle the different operations that can be performed on that resource.
project/
├── controllers/
│ ├── userController.js
│ └── authController.js
├── routes/
│ ├── userRoutes.js
│ └── authRoutes.js
└── app.js
In this example, the controllers
folder contains two files, userController.js
and authController.js
, which defines the functions that implement the business logic for the users
and auth
resources, respectively.
The routes
folder contains two files, userRoutes.js
and authRoutes.js
, which define the endpoints and HTTP methods for the users
and auth
resources, respectively.
Finally, the app.js
file is the main entry point for your Node.js application, and it imports the userRoutes.js
and authRoutes.js
files to define the endpoints for your API.
Conclusion
We have seen how controllers are an important part of building maintainable and scalable applications in Node.js. They help to separate concerns and make the codebase more modular and easier to maintain.
I’ve also written elaborated articles on Middlewares and Expressjs routes to make things more understandable. Check them out and if you’re having questions, put them in the comments.