Reputation: 1013
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?
Upvotes: 2
Views: 2131
Reputation: 203484
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)
Upvotes: 4