DEV Community

Cover image for Stop using express.Router()
Matthias
Matthias

Posted on

Stop using express.Router()

Ever wrote code like this? Stop it.

// 🚨 this is messy
const router = express.Router()

router.get("/posts", async (req, res) => {
    res.json([])
})

export default router
Enter fullscreen mode Exit fullscreen mode

One of the great features that come with modern frontend frameworks like Next.js is a /pages directory where all your app's routes live in.

Luckily, we can achieve the same DX in our Express backend codebases too without writing our own ugly express.Router()s but with the express-file-routing package.

// ✅ this is enjoyable
export const get = async (req, res) => {
    res.json([])
}
Enter fullscreen mode Exit fullscreen mode

As you can see, it's a super clean approach and quite beneficial for code style and maintainability to split your Express route handlers into separate files.

Just by taking a look at a project's file structure, one can intuitively see what endpoints our API is about to provide.

├── app.ts
├── routes
    └── posts
        ├── index.ts
        └── [id].ts
└── package.json
Enter fullscreen mode Exit fullscreen mode
  • /routes/posts/index.ts → /posts
  • /routes/posts/[id].ts → /posts/:id

How to use Express file routing

This is quite of a straightforward process. Even though this post is more of an intro rather than a tutorial on how to use this express-file-routing, here's a quick start.

All you need to do is to install express-file-routing from npm.

npm install express-file-routing
Enter fullscreen mode Exit fullscreen mode

In your main entry file, create an Express app instance and attach the file router.

// app.ts
import express from "express"
import { router } from "express-file-routing"

const app = express()

app.use("/", router()) // this is the magic

app.listen(4000)
Enter fullscreen mode Exit fullscreen mode

You're already done!

Enqueuing new routes from now on is as easy as creating a file in /routes and exporting your HTTP method handlers.

// /routes/posts.ts
export const get = async (req, res) => {
    res.json([])
}

export const post = async (req, res) => {
    await db.post.create()
    res.status(201)
}
Enter fullscreen mode Exit fullscreen mode

Even middlewares are supported by exporting an array of request handlers instead of a single one.

export const post = [
  rateLimit(), userAuth(),
  async (req, res) => {
    res.status(201).json({})
  }
]
Enter fullscreen mode Exit fullscreen mode

For detailed docs check out https://github.com/matthiaaas/express-file-routing.

Thanks for reading and stop using express.Router() ever again.

Top comments (2)

Collapse
 
bartekus profile image
Bartekus

Perhaps by providing reason why using express.Router() is bad (beyond aesthetics), this approach could gain more traction. Just my 2 cents.

Collapse
 
matthiaaas profile image
Matthias

Thank you for your feedback @bartekus. I don't think express.Router() is bad per se. It absolutely legitimately exists. I came up with this package for another reason. File-based routing becomes more and more of an industry standard in the JS web framework community because of its benefits for developers when working in a team but also on solo projects.
File routing is much more intuitive, structured & effortlessly scalable. You won't need to bother about exporting & importing your router instances over and over again and making sure it's enqueued in the top level of your root app instance.
This is less about "aesthetics" rather than modularity.