← Back to all tutorials

Creating Routes

Organize your API into modular route files using Express Router — keep your code clean and maintainable as the API grows.

Creating Routes

As your API grows, putting all routes in a single file becomes unmanageable. Express provides the Router class that lets you group related routes into separate files. In this episode you will organize your API into modular, maintainable route files.

The Problem: Everything in One File

// index.js — this gets messy fast!
app.get('/api/ninjas', ...);
app.post('/api/ninjas', ...);
app.get('/api/ninjas/:id', ...);
app.put('/api/ninjas/:id', ...);
app.delete('/api/ninjas/:id', ...);
app.get('/api/weapons', ...);
app.post('/api/weapons', ...);
// ... dozens more routes

Express Router

The express.Router() creates a mini Express application that can have its own routes and middleware. You define routes in separate files and mount them on the main app.

Step 1: Create the Routes Folder

rest-api/
├── routes/
│   └── ninjas.js       (ninja routes)
├── index.js
└── package.json

Step 2: Define Routes in a Router File

routes/ninjas.js

const express = require('express');
const router = express.Router();

// GET all ninjas
router.get('/', (req, res) => {
    res.json({ message: 'GET all ninjas' });
});

// GET a single ninja
router.get('/:id', (req, res) => {
    res.json({ message: 'GET ninja ' + req.params.id });
});

// POST a new ninja
router.post('/', (req, res) => {
    res.json({ message: 'POST a new ninja', data: req.body });
});

// PUT (update) a ninja
router.put('/:id', (req, res) => {
    res.json({ message: 'PUT ninja ' + req.params.id });
});

// DELETE a ninja
router.delete('/:id', (req, res) => {
    res.json({ message: 'DELETE ninja ' + req.params.id });
});

module.exports = router;

Notice that routes use / and /:id instead of /api/ninjas and /api/ninjas/:id. The prefix is defined when the router is mounted.

Step 3: Mount the Router in index.js

const express = require('express');
const ninjaRoutes = require('./routes/ninjas');

const app = express();

app.use(express.json());

// Mount the ninja routes at /api/ninjas
app.use('/api/ninjas', ninjaRoutes);

const PORT = 3000;
app.listen(PORT, () => {
    console.log(`Server running on port ${PORT}`);
});

The app.use('/api/ninjas', ninjaRoutes) line mounts the router at /api/ninjas. All routes defined in the router are prefixed with this path:

Router RouteMounted PathFull URL
router.get('/')/api/ninjasGET /api/ninjas
router.get('/:id')/api/ninjasGET /api/ninjas/123
router.post('/')/api/ninjasPOST /api/ninjas
router.delete('/:id')/api/ninjasDELETE /api/ninjas/123

Adding More Route Files

// routes/weapons.js
const router = express.Router();
router.get('/', (req, res) => { ... });
module.exports = router;

// index.js
const weaponRoutes = require('./routes/weapons');
app.use('/api/weapons', weaponRoutes);

Each resource gets its own route file. The main index.js stays clean — it only imports and mounts routers.

Route Files with Method Chaining

router.route('/')
    .get((req, res) => {
        res.json({ message: 'GET all ninjas' });
    })
    .post((req, res) => {
        res.json({ message: 'POST a new ninja' });
    });

router.route('/:id')
    .get((req, res) => {
        res.json({ message: 'GET ninja ' + req.params.id });
    })
    .put((req, res) => {
        res.json({ message: 'PUT ninja ' + req.params.id });
    })
    .delete((req, res) => {
        res.json({ message: 'DELETE ninja ' + req.params.id });
    });

router.route() lets you chain multiple methods on the same path — reducing repetition and grouping related handlers together.

Key Takeaways

  • express.Router() creates a modular, mountable set of routes
  • Define routes in separate files under a routes/ folder for clean organization
  • Mount routers with app.use('/prefix', router) — the prefix is prepended to all router routes
  • Routes inside the router use relative paths (/, /:id) — the prefix is added by app.use()
  • router.route() chains multiple HTTP methods on the same path