Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Router and routes in separate files

When every route lives in src/app.js, the file grows quickly. express.Router is a mini application: you describe a group of routes in a separate file and then mount it on the main application under a common address.

A routes file

Create the file src/routes/posts.js:

// src/routes/posts.js
import { Router } from 'express';
 
const router = Router();
 
router.get('/', (req, res) => {
  res.send('List of posts');
});
 
router.get('/:id', (req, res) => {
  res.send(`Post ${req.params.id}`);
});
 
export default router;

A router has the same methods as the application: get, post, delete and others. Addresses here are relative to where the router is mounted.

Mounting it

In src/app.js import the router and mount it with app.use():

// src/app.js
import express from 'express';
import postsRouter from './routes/posts.js';
 
const app = express();
 
app.use('/posts', postsRouter);
 
export default app;

Now /posts calls the router's '/' route, and /posts/2 calls '/:id'. If the section moves to /articles, you change one line in app.js.

Controllers

To keep the routes file a short list of addresses, the handlers themselves are moved into a controller. Create src/controllers/postsController.js:

// src/controllers/postsController.js
import { posts } from '../data/posts.js';
 
export function index(req, res) {
  res.send(posts.map((post) => post.title).join(', '));
}
 
export function show(req, res) {
  const post = posts.find((item) => item.id === Number(req.params.id));
 
  if (!post) {
    return res.status(404).send('Post not found');
  }
 
  res.send(post.title);
}

And replace the functions in the router with references to them:

// src/routes/posts.js
import { Router } from 'express';
import * as postsController from '../controllers/postsController.js';
 
const router = Router();
 
router.get('/', postsController.index);
router.get('/:id', postsController.show);
 
export default router;

The pages /posts and /posts/1 work as before, but now one look at routes/posts.js shows every address of the section.

Parent parameters

If a router is mounted at an address with a parameter, for example '/posts/:postId/comments', it does not see postId by default. The mergeParams option passes the parent's parameters through:

// src/routes/comments.js
import { Router } from 'express';
 
const router = Router({ mergeParams: true });
 
router.get('/', (req, res) => {
  res.send(`Comments on post ${req.params.postId}`);
});
 
export default router;

In app.js it is mounted like this: app.use('/posts/:postId/comments', commentsRouter).

Common mistake

Do not repeat the address inside the router. If you write router.get('/posts/:id') and mount the router with app.use('/posts', ...), the route is reachable only at /posts/posts/2. The common part of the address is given once — in app.use().

Thanks for reading!

Donate to the site — we work hard for you.