Express Routers and Controllers

Registered members can download the FREE Get Started App. This is the project I used to compose articles about setting up VS Code and developing Node with Express and the Embedded JavaScript (EJS) view engine.

Routers handle the request endpoints. Controllers handle the response. This article describes how routers and controllers implement the separation of concerns principle.

When I decided to learn Express, I found a lot of articles and AI suggestions which did not implement ES6 standards. ES6 or ECMAScript 2015 changed the landscape of JavaScript with the ability to create promises for asynchronous programming. I am familiar with ASP.NET Core and C# web applications and SQL Server databases. I developed this Express application with KenHaggerty.Com as a model. Registered members can download the FREE Get Started PostgreSQL app and Get Started MySQL app which implement this getting started with Express EJS tutorial.

So far, we have been implementing routes in server.js. Express.js routers provide a way to organize application routes and middleware in a modular fashion. You can implement multiple routers to manage separate interests like public access, authenticated access, and administrator access. This separation makes larger applications easier to manage, maintain, and test as the codebase grows.

I assume you have VS Code configured, if not, please see the Development Environment Setup article. From the Explorer tab in VS Code, add a routers folder to the root directory. Then add a file named root-router.mjs to the routers folder. Add the ES6 import and export statements to root-router.mjs. Then migrate any routes from server.js to root-router.mjs. Be sure to update the app handler to the rootRouter handler.

root-router.mjs
import express from 'express';
const rootRouter = express.Router();

rootRouter.get('/', (req, res) => {
  const articleLinks = [
    { title: 'Let\'s Talk About Express', titleslug: 'lets-talk-about-express' },
    { title: 'Development Environment Setup', titleslug: 'development-environment-setup' },
    { title: 'Features Since ECMAScript 2015(ES6)', titleslug: 'features-since-ecmascript-2015es6' },
    { title: 'Express EJS Template Engine', titleslug: 'express-ejs-template-engine' },
    { title: 'Express EJS Views, Layouts, and Partials', titleslug: 'express-ejs-views-layouts-and-partials' },
    { title: 'Express Error Handling', titleslug: 'express-error-handling' },
    { title: 'Express Routers and Controllers', titleslug: 'express-routers-and-controllers' },
    { title: 'MySQL and Services', titleslug: 'mysql-and-services' }
  ];
  const templateData = {
    pageTitle: 'Get Started With EJS',
    pageSubTitle: 'Added error page, error handling middleware, and error testing.',
    articleLinks
  }
  res.render('pages/index', templateData);
})

rootRouter.get('/about', (req, res) => {
  const templateData = {
    pageTitle: 'About View',
    pageSubTitle: 'Use this view to describe yourself.'
  }
  res.render('pages/about', templateData);
})

export default rootRouter;

You implement the router in server.js. Import the router to server.js.

server.js
import rootRouter from './routers/root-router.mjs';

Before the not found error handler, set the middleware to use the router. You can use the path parameter to map different routers. If the path parameter is set to "/account" all requests which start with account can be handled by an user-account-router.mjs.

server.js
// Use route modules
app.use('/', rootRouter);

You should verify the routes are functioning properly before you separate concerns even further by implementing controllers. In Express.js, controllers are a powerful pattern that allows you to keep your code modular and maintainable by separating the logic that handles requests and responses from the routes themselves. Add a controllers folder to the root directory. Then add a file named root-controller.mjs to the controllers folder. Migrate the route logic to functions in the controller.

root-controller.mjs
const RootController = {};

RootController.GetHomePage = (req, res) => {
    const articleLinks = [
        { title: 'Let\'s Talk About Express', titleslug: 'lets-talk-about-express' },
        { title: 'Development Environment Setup', titleslug: 'development-environment-setup' },
        { title: 'Features Since ECMAScript 2015(ES6)', titleslug: 'features-since-ecmascript-2015es6' },
        { title: 'Express EJS Template Engine', titleslug: 'express-ejs-template-engine' },
        { title: 'Express EJS Views, Layouts, and Partials', titleslug: 'express-ejs-views-layouts-and-partials' },
        { title: 'Express Error Handling', titleslug: 'express-error-handling' },
        { title: 'Express Routers and Controllers', titleslug: 'express-routers-and-controllers' },
        { title: 'MySQL and Services', titleslug: 'mysql-and-services' }
    ];
    const templateData = {
        pageTitle: 'Get Started With EJS',
        pageSubTitle: 'Added error page, error handling middleware, and error testing.',
        articleLinks
    }
    res.render('pages/index', templateData);
}
RootController.GetAboutPage = (req, res) => {
    const templateData = {
        pageTitle: 'About View',
        pageSubTitle: 'Use this view to describe yourself.'
    }
    res.render('pages/about', templateData);
}

export default RootController;

Import the RootController to the rootRouter.mjs. Update the routes to call the associated controller function.

root-router.mjs
import RootController from '../controllers/root-controller.mjs';
import express from 'express';
const rootRouter = express.Router();

rootRouter.get('/', RootController.GetHomePage);
rootRouter.get('/about', RootController.GetAboutPage);

export default rootRouter;
Add Router and Controller
Created: 2/14/26