Search code examples
regexexpressurl-routing

Make separator optional in express router


My urls may either look like /posts/sluggish-slug-postId123 or like /posts/postId123.

My router currently contains:

router.get('/posts/:slug?-:id([a-zA-Z0-9]+)', function () {...}

but using this regex the - in between the slug and the id is mandatory, which means I can't validate urls that only contain an id (e.g. /posts/postId123). How do I make the - separator optional along with keeping the slug optional?

Paths that should match

  • /posts/going-long-for-the-ball-3a6p412
    • slug: going-long-for-the-ball
    • id: 3a6p412
  • /posts/3a6p412
    • slug: not set
    • id: 3a6p412

Paths that should not match

  • /posts/-3a6p412

Solution

  • I couldn't quite get what you wanted but this comes close:

    router.get('/posts/:slug([a-z][a-z\-]{0,}-|):id([a-zA-Z0-9]+)', function (req, res) {
    

    The slug will include the trailing - if it's present and it will be an empty string if there is no slug. The section [a-z][a-z\-]{0,} ensures that the slug must start with a character in the range a-z and can then contain any number of characters from a-z or -. If your slugs can contain other characters you'd just need to adapt that part accordingly. Note that I'm using {0,} rather than * because of a quirk of how Express routes interprets *.

    The way Express routes get converted to regular expressions is somewhat difficult to follow and there are major changes between Express 4 and 5 that make using complex expressions an unsafe bet for future compatibility. If you want to do something more complicated than the examples given in the documentation I would suggest just using an actual RegExp instead to cut out the unreliable middleman. Alternatively you could skip trying to parse/validate the URL in the route path altogether and do that inside your handler function instead.