Q:

Node Express - Route path colon parameter exception

Currently I have two routes in my app:
/invoice/:invoice returns JSON data of an Invoice document from Mongoose
/invoice/preview returns a preview of an invoice inside an HTML template (note that this doesn't always preview an existing invoice, it could also be a non-existing of which its data is supplied via url parameters, which is why the route cannot be /invoice/:invoice/preview)
Question
There should be a better way to declare these two specific routes, because the /invoice/preview route now calls both handlers, since it matches both regexes.
If we were talking in CSS selectors /invoice/:invoice:not(preview) would be the behavior I want. Unfortunately I don't find any documentation for this. 
Is there any way to achieve this or any way to improve this endpoint structure?

A:

Declare more specific routes first:
router.get('/invoice/preview', ...);

router.get('/invoice/:invoice', ...);

Express checks routes in order of declaration, so once it has matched a request against /invoice/preview (and provided that its handler sends back a response), the less-specific /invoice/:invoice won't be considered.
Alternatively, if :invoice should always match a specific pattern (say a MongoDB ObjectId), you can limit the route to requests matching that pattern:
router.get('/invoice/:invoice([a-fA-F0-9]{24})', ...);

That pattern doesn't match "preview", so the order wouldn't matter so much in that case.
If this isn't possible, you could create a middleware that would check if req.params.invoice matches "preview" and, if so, would pass along the request further down the handler chain:
let notIfPreview = (req, res, next) => {
  if (req.params.invoice === 'preview') return next('route');
  next();
};

router.get('/invoice/:invoice', notIfPreview, ...);
router.get('/invoice/preview', ...);

(documented here)

