Home > OS >  How to properly route different paths with ExpressJS/ NodeJS?
How to properly route different paths with ExpressJS/ NodeJS?

Time:05-05

I initially had a problem where I had two separate and distinct paths for my nodeJS server like so:

Path A to fetch all businesses near latlng coordinates:

app.get("/:lat/:lng/", (req, res) => {

Path B to fetch review for specific business.

app.get('/businesses/:businessID/reviews', (req, res)=>{

As soon as I modify Path A to add a category parameter:

app.get("/:lat/:lng/:searchCategory", (req, res) => {

Path B throws error code 400 and no longer fetches reviews. I figured out a "bandaid" solution by just extending the length of parameters in my Path A like so:

app.get("/:lat/:lng/category/:searchCategory", (req, res) => {

Everything is working as expected now but I want to know what is happening and why. I couldn't really find an explanation. It seems like I'm following all the ExpressJS docs and rules. Any ideas would be appreciated. Thank you!

CodePudding user response:

Routes that consist entirely of wildcards (specifications that match ANYTHING) are generally a problem in Express. While an extremely careful design can sometimes make this work, it is very easy to run into conflicts with other routes because wildcard routes match ANYTHING, even other things that you want to use as different routes. In addition, these top level wildcard routes often cause future design and expansion problems because if you want to then add new top level routes in the future, your options may be limited because of the previous wildcard routes that you allowed.

The safest option and simplest option is to NOT use top level wildcard routes at all. Always use some non-wildcard prefix on each route like this:

app.get("/loc/:lat/:lng/", ...);

Since this is not a top level wildcard, this will not conflict with any other top level routes as long as they don't start with /loc.

You could then also have these to go with it and none would conflict:

app.get("/loc/:lat/:lng/:searchCategory", ...)
app.get('/businesses/:businessID/reviews', ...)

Note, the common theme here is that each family of routes has its own top level non-wildcard path segment. In the case of these three routes, they use /loc and /businesses and that clearly separates them so they won't conflict. Then, within /loc, you have one with two parameters and one with three parameters so those don't conflict.


If you want to know why these two conflict:

app.get("/:lat/:lng/:searchCategory", ...)    
app.get('/businesses/:businessID/reviews', ...)

it's because both are three parameter URLs and the first one accepts three wildcards so it matches ANY three parameter URL, not just URLs that actually contain lat and long values.

Technically, you could reverse these definitions to get them separated:

app.get('/businesses/:businessID/reviews', ...)
app.get("/:lat/:lng/:searchCategory", ...)    

And, this would work, but creating a URL structure that conflicts in principle and only works if you define all the routes in the exact right order is a maintenance headache going forward and is prone to breaking with one simple change in the code or is prone to getting boxed in sometime in the future when you can't extend your URL structure in the way you want because of the past wildcard routes.

So, my advice is to avoid top level wildcard routes. If each URL that contains a wildcard has it's own non-wildcard top level specifier, then all wildcard routes are uniquely separated and you don't have conflicts or ordering issues.

  • Related