Express Route to Regular Expression Converter
Convert Express.js route patterns to regular expressions. Extract named parameters, support wildcards, optional params, and custom patterns.
About
Express.js routes like /user/:id are syntactic sugar over regular expressions. Underneath, Express uses the path-to-regexp module to compile route strings into RegExp objects with ordered capture groups. Misunderstanding how a route compiles leads to unintended matches: a route /:file.:ext without the end flag will match paths you never intended, leaking data or bypassing middleware. This tool implements the same tokenization and compilation algorithm that Express 4.x uses internally.
The converter handles named parameters (:param), optional segments (:param?), zero-or-more wildcards (*), custom capture patterns (:id(\d+)), and unnamed groups. Flags for strict mode (trailing slash sensitivity), end anchoring, and case sensitive matching replicate Express router configuration exactly. Note: this replicates Express 4.x behavior. Express 5.x migrated to path-to-regexp v6 with breaking syntax changes.
Formulas
The conversion algorithm operates in two phases: tokenization and compilation. During tokenization, the route string is scanned character by character to extract tokens of type literal, parameter, or wildcard.
tokenize(route) → [T0, T1, …, Tn]
where each token Ti = { name, prefix, delimiter, optional, repeat, pattern }
During compilation, each token is converted into a regex fragment. A named parameter :param becomes the capture group ([^\/]+?). A repeating parameter :param+ becomes ((?:[^\/]+?)(?:\/(?:[^\/]+?))*). Literal characters are escaped with escapeString which prefixes regex metacharacters with \.
compile(tokens, options) → RegExp
route = "^" + n∑i=0 fragment(Ti) + anchor
Where anchor = "$" when options.end is TRUE, and empty string otherwise. The strict flag controls whether a trailing / is optional. The sensitive flag toggles the i regex flag.
Reference Data
| Route Pattern | Compiled RegExp | Keys Extracted | Matches Example |
|---|---|---|---|
/ | /^\/?$/i | - | / |
/users | /^\/users\/?$/i | - | /users |
/users/:id | /^\/users\/([^\/]+?)\/?$/i | id | /users/42 |
/users/:id? | /^\/users(?:\/([^\/]+?))?\/?$/i | id (optional) | /users, /users/42 |
/files/* | /^\/files\/(.*?)\/?$/i | 0 (unnamed) | /files/a/b/c.txt |
/user/:id/post/:postId | /^\/user\/([^\/]+?)\/post\/([^\/]+?)\/?$/i | id, postId | /user/5/post/99 |
/item/:id(\d+) | /^\/item\/(\d+)\/?$/i | id | /item/123 (not /item/abc) |
/file.:ext | /^\/file\.([^\/]+?)\/?$/i | ext | /file.json |
/page/:slug-:id | /^\/page\/([^\/]+?)-([^\/]+?)\/?$/i | slug, id | /page/hello-42 |
/api/v:version/data | /^\/api\/v([^\/]+?)\/data\/?$/i | version | /api/v2/data |
/:foo+ | /^\/((?:[^\/]+?)(?:\/(?:[^\/]+?))*)\/?$/i | foo (one-or-more) | /a/b/c |
/:foo* | /^(?:\/((?:[^\/]+?)(?:\/(?:[^\/]+?))*))?\/?$/i | foo (zero-or-more) | /, /a/b |
/route(s)? | /^\/route(?:s)?\/?$/i | - | /route, /routes |
/fly/:from-:to | /^\/fly\/([^\/]+?)-([^\/]+?)\/?$/i | from, to | /fly/LAX-JFK |
/download/:filepath* | /^\/download(?:\/((?:[^\/]+?)(?:\/(?:[^\/]+?))*))?\/?$/i | filepath | /download/docs/readme.md |
Frequently Asked Questions
:param matches exactly one path segment and is required. Adding ? makes it optional - the route matches with or without that segment. The + modifier makes the parameter match one or more segments (e.g., /files/:path+ matches /files/a/b/c), producing a slash-joined string. The * modifier matches zero or more segments, making the entire parameter optional while still allowing multi-segment capture. These map directly to regex quantifiers: ? becomes ?, + becomes ((?:segment)(?:/(?:segment))*), and * wraps the whole group in an optional non-capturing group.strict is false (the default in Express), the compiled regex appends an optional trailing slash \/? before the end anchor. This means /users matches both /users and /users/. When strict is true, the trailing slash is not made optional - /users matches only /users, and /users/ would not match. This matters for SEO canonical URLs and API consistency.end flag determines whether the regex is anchored at the end with $. When true (default), /users matches only /users exactly. When false, /users also matches /users/123/posts - it becomes a prefix match. Express uses end: false internally for app.use() middleware mounting, which is why middleware on /api fires for all /api/* routes.:id(\d+)), it uses the contents of the parentheses as the capture pattern instead of the default [^\/]+?. The compiled regex becomes \/(\d+) for that segment. This is powerful for type-constraining route parameters at the routing level. The key name id is still extracted and stored. If the custom pattern contains unbalanced parentheses, the tokenizer will throw a parse error.path-to-regexp v0.1.x, which is what the vast majority of Express applications use in production. Express 5.x uses path-to-regexp v6+ which introduces breaking changes: the wildcard syntax changes from * to (.*) or named :splat*, optional parameters use {:param} syntax, and unnamed capture groups are no longer supported. If you are on Express 5, the generated regex from this tool may not match your router behavior.\/? doesn't need capturing. A repeating parameter like :path+ uses (?:\/(?:[^\/]+?))* for the repetition part - only the outer group captures the full multi-segment match. Optional literal groups like (s)? in a route compile to (?:s)? to avoid polluting the req.params array with unnecessary matches.