When You Need More Than Simple Patterns
Regex Routing
Express accepts strings, parameterized patterns, and raw regex. Regex is overkill for most cases — but useful for some.
What you'll learn
- Constrain params with regex
- Use raw regex as a path
- Know when to step back
app.get accepts a few path types:
- A literal string:
/users - A parameterized string:
/users/:id - A parameterized string with a regex constraint:
/users/:id(\\d+) - A raw
RegExp:/^\/users\/\d+$/
Constraining a Parameter
Limit :id to digits only:
app.get("/users/:id(\\d+)", (req, res) => {
res.json({ id: Number(req.params.id) });
}); GET /users/42 matches. GET /users/me doesn’t (falls through to
the next route or 404).
A common alphanumeric ID pattern (UUIDs, e.g.):
const UUID_RE = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";
app.get(`/users/:id(${UUID_RE})`, getUser); Now only UUID-shaped IDs match.
Raw Regex
For routes that defy normal patterns:
app.get(/^\/legacy\/(\d{4})\/(\d{2})$/, (req, res) => {
const [year, month] = req.params; // numeric indices
res.json({ year, month });
}); With raw regex, captures land on req.params[0], req.params[1],
etc. — numeric, not named.
When To Use It
- API versioning:
/api/v(\\d+)/usersto constrain to digits - File extensions:
/files/:name(.+).(jpg|png)(though static middleware is usually better) - Migration: matching legacy URL formats during a rewrite
When NOT To
Most of the time, named params and explicit routes are clearer:
// regex — clever but cryptic
app.get(/^\/posts\/(\d+)$/, getPostById);
// explicit — boring but obvious
app.get("/posts/:id(\\d+)", getPostById); If someone reads your route file and has to consult regex docs, you’ve added a hurdle.
Escape Carefully
In a string regex constraint, you escape with \\. In a real
RegExp, you use \. Easy to mix up.