Skip to content

Commit bb571d8

Browse files
Akshay4754ShubhamOulkarbjohansebas
authored
docs(routing): clarify wildcard behavior and restructure route parameters (#2451)
Signed-off-by: Akshay Anand <147163632+Akshay4754@users.noreply.github.com> Co-authored-by: shubham oulkar <oulkarshubhu@gmail.com> Co-authored-by: Sebastian Beltran <bjohansebas@gmail.com>
1 parent 2851d08 commit bb571d8

3 files changed

Lines changed: 148 additions & 78 deletions

File tree

src/content/api/5x/api/request/index.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,8 @@ app.get('/files/*file', (req: Request, res: Response) => {
425425
});
426426
```
427427

428+
Parameters defined in [optional segments](/guide/routing/#optional-segments) that are not present in the request URL are omitted from `req.params` entirely.
429+
428430
When you use a regular expression for the route definition, capture groups are provided as integer keys using `req.params[n]`, where `n` is the n<sup>th</sup> capture group.
429431

430432
```js

src/content/docs/en/4x/guide/routing.mdx

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ app.all('/secret', (req: Request, res: Response, next: NextFunction) => {
108108

109109
## Route paths
110110

111-
Route paths, in combination with a request method, define the endpoints at which requests can be made. Route paths can be strings, string patterns, or regular expressions.
111+
Route paths, in combination with a request method, define the endpoints at which requests can be made. Route paths can be strings, string patterns, or regular expressions. They can also capture values from the URL, as described in [Route parameters](#route-parameters) below.
112112

113113
<Alert type="info">
114114

@@ -235,6 +235,26 @@ app.get('/ab(cd)?e', (req: Request, res: Response) => {
235235
});
236236
```
237237

238+
A wildcard (`*`) on its own matches anything at its position, including entire subpaths. For example, this route path will match `/file/style.css` as well as `/file/javascripts/jquery.js`. Wildcards are unnamed, so the matched value is available as `req.params[0]` instead of a named parameter.
239+
240+
```js
241+
app.get('/file/*', (req, res) => {
242+
// GET /file/javascripts/jquery.js
243+
res.send(req.params[0]);
244+
// => 'javascripts/jquery.js'
245+
});
246+
```
247+
248+
```ts
249+
import { type Request, type Response } from 'express';
250+
251+
app.get('/file/*', (req: Request, res: Response) => {
252+
// GET /file/javascripts/jquery.js
253+
res.send(req.params[0]);
254+
// => 'javascripts/jquery.js'
255+
});
256+
```
257+
238258
### Route paths based on regular expressions
239259

240260
<Alert type="alert">
@@ -348,6 +368,14 @@ characters with an additional backslash, for example `\\d+`.
348368
The [`*`](https://github.com/expressjs/express/issues/2495) character in regular expressions is not interpreted in the usual way. As a workaround, use `{0,}` instead of `*`.
349369
</Alert>
350370

371+
Unlike named route parameters, wildcard (`*`) matches in [string patterns](#route-paths-based-on-string-patterns) and capture groups in regular expressions are unnamed: their values are available by position, as `req.params[0]`, `req.params[1]`, and so on.
372+
373+
```
374+
Route path: /file/*/size/*
375+
Request URL: http://localhost:3000/file/javascripts/jquery.js/size/large
376+
req.params: { "0": "javascripts/jquery.js", "1": "large" }
377+
```
378+
351379
## Route handlers
352380

353381
You can provide multiple callback functions that behave like [middleware](/guide/using-middleware) to handle a request. The only exception is that these callbacks might invoke `next('route')` to bypass the remaining route callbacks. You can use this mechanism to impose pre-conditions on a route, then pass control to subsequent routes if there's no reason to proceed with the current route.
@@ -544,7 +572,7 @@ The methods on the response object (`res`) in the following table can send a res
544572
## app.route()
545573

546574
You can create chainable route handlers for a route path by using `app.route()`.
547-
Because the path is specified at a single location, creating modular routes is helpful, as is reducing redundancy and typos. For more information about routes, see: [Router() documentation](/api/router).
575+
Because the path is specified in a single location, this helps to create modular routes and reduces redundancy and typos. For more information about routes, see the [Router() documentation](/api/router).
548576

549577
Here is an example of chained route handlers that are defined by using `app.route()`.
550578

@@ -677,7 +705,7 @@ app.use('/birds', birds);
677705

678706
The app will now be able to handle requests to `/birds` and `/birds/about`, as well as call the `timeLog` middleware function that is specific to the route.
679707

680-
But if the parent route `/birds` has path parameters, it will not be accessible by default from the sub-routes. To make it accessible, you will need to pass the `mergeParams` option to the Router constructor [reference](/api/application#appuse).
708+
But if the parent route `/birds` has path parameters, it will not be accessible by default from the sub-routes. To make it accessible, you will need to pass the `mergeParams` option to the [Router constructor](/api/express/#expressrouter).
681709

682710
```js
683711
const router = express.Router({ mergeParams: true });

src/content/docs/en/5x/guide/routing.mdx

Lines changed: 115 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ app.all('/secret', (req: Request, res: Response, next: NextFunction) => {
108108

109109
## Route paths
110110

111-
Route paths, in combination with a request method, define the endpoints at which requests can be made. Route paths can be strings or regular expressions.
111+
Route paths, in combination with a request method, define the endpoints at which requests can be made. Route paths can be strings or regular expressions. They can also capture values from the URL, as described in [Route parameters](#route-parameters) below.
112112

113113
<Alert type="info">
114114

@@ -153,77 +153,9 @@ app.get('/random.text', (req: Request, res: Response) => {
153153
});
154154
```
155155

156-
### Wildcards
157-
158-
Wildcards match any path after a prefix. They must have a name, just like route parameters, and are captured as arrays of path segments.
159-
160-
```js
161-
app.get('/files/*filepath', (req, res) => {
162-
// GET /files/images/logo.png
163-
console.dir(req.params.filepath);
164-
// => [ 'images', 'logo.png' ]
165-
res.send(`File: ${req.params.filepath.join('/')}`);
166-
});
167-
```
168-
169-
```ts
170-
import { type Request, type Response } from 'express';
171-
172-
app.get('/files/*filepath', (req: Request<{ filepath: string[] }>, res: Response) => {
173-
// GET /files/images/logo.png
174-
console.dir(req.params.filepath);
175-
// => [ 'images', 'logo.png' ]
176-
res.send(`File: ${req.params.filepath.join('/')}`);
177-
});
178-
```
179-
180-
To also match the root path, wrap the wildcard in braces:
181-
182-
```js
183-
// Matches / , /foo , /foo/bar , etc.
184-
app.get('/{*splat}', (req, res) => {
185-
// GET / => req.params.splat = []
186-
// GET /foo/bar => req.params.splat = [ 'foo', 'bar' ]
187-
res.send('ok');
188-
});
189-
```
190-
191-
```ts
192-
import { type Request, type Response } from 'express';
193-
194-
// Matches / , /foo , /foo/bar , etc.
195-
app.get('/{*splat}', (req: Request, res: Response) => {
196-
// GET / => req.params.splat = []
197-
// GET /foo/bar => req.params.splat = [ 'foo', 'bar' ]
198-
res.send('ok');
199-
});
200-
```
201-
202-
### Optional segments
203-
204-
Use braces to define optional segments in a route path. When the segment is not present, the parameter is omitted from `req.params`.
205-
206-
```js
207-
app.get('/:file{.:ext}', (req, res) => {
208-
// GET /image.png => req.params = { file: 'image', ext: 'png' }
209-
// GET /image => req.params = { file: 'image' }
210-
res.send('ok');
211-
});
212-
```
213-
214-
```ts
215-
import { type Request, type Response } from 'express';
216-
217-
app.get('/:file{.:ext}', (req: Request, res: Response) => {
218-
// GET /image.png => req.params = { file: 'image', ext: 'png' }
219-
// GET /image => req.params = { file: 'image' }
220-
res.send('ok');
221-
});
222-
```
223-
224156
<Alert type="alert">
225157

226-
The characters `?`, `+`, `*`, `[]`, and `()` are reserved and cannot be used as literal characters in route paths. Use `\` to escape them if needed.
158+
The characters `?`, `+`, `*`, `[]`, `()`, and `!` are reserved and cannot be used as literal characters in route paths, and braces are reserved for [optional segments](#optional-segments). Use `\` to escape them if needed.
227159

228160
</Alert>
229161

@@ -259,7 +191,11 @@ app.get(/.*fly$/, (req: Request, res: Response) => {
259191

260192
## Route parameters
261193

262-
Route parameters are named URL segments that are used to capture the values specified at their position in the URL. The captured values are populated in the `req.params` object, with the name of the route parameter specified in the path as their respective keys.
194+
Route parameters are named URL segments that are used to capture the values specified at their position in the URL. The captured values are populated in the `req.params` object, with the name of the route parameter specified in the path as their respective keys. They come in three forms: [named parameters](#named-parameters) (`:name`), [wildcards](#wildcards) (`*name`), and [optional segments](#optional-segments), which wrap either of them in braces.
195+
196+
### Named parameters
197+
198+
Named parameters capture a single path segment at their position in the URL, or part of one when combined with literal characters, as shown further below.
263199

264200
```
265201
Route path: /users/:userId/books/:bookId
@@ -293,7 +229,7 @@ app.get('/users/:userId/books/:bookId', sendParams);
293229

294230
<Alert type="alert">
295231

296-
The name of route parameters must be made up of "word characters" ([A-Za-z0-9_]).
232+
The name of route parameters must be a valid JavaScript identifier. Other names can be used by quoting them, for example `:"user-name"`.
297233

298234
</Alert>
299235

@@ -313,11 +249,115 @@ req.params: { "genus": "Prunus", "species": "persica" }
313249

314250
<Alert type="alert">
315251

316-
Regexp characters are not supported in route paths. Use an array of paths or regular expressions instead.
252+
Regexp characters are not supported inside string paths, so a parameter cannot be restricted with a suffix such as `:userId(\d+)`. Use an array of paths or a full regular expression instead.
317253
See the [path route matching syntax](/guide/migrating-5#path-route-matching-syntax) for more information.
318254

319255
</Alert>
320256

257+
### Wildcards
258+
259+
Wildcards match any path after a prefix. Like other route parameters they must have a name, but they are captured as an array of path segments instead of a string.
260+
261+
```js
262+
app.get('/files/*filepath', (req, res) => {
263+
// GET /files/images/logo.png
264+
console.dir(req.params.filepath);
265+
// => [ 'images', 'logo.png' ]
266+
res.send(`File: ${req.params.filepath.join('/')}`);
267+
});
268+
```
269+
270+
```ts
271+
import { type Request, type Response } from 'express';
272+
273+
app.get('/files/*filepath', (req: Request<{ filepath: string[] }>, res: Response) => {
274+
// GET /files/images/logo.png
275+
console.dir(req.params.filepath);
276+
// => [ 'images', 'logo.png' ]
277+
res.send(`File: ${req.params.filepath.join('/')}`);
278+
});
279+
```
280+
281+
To also match the root path, wrap the wildcard in braces:
282+
283+
```js
284+
// Matches / , /foo , /foo/bar , etc.
285+
app.get('/{*splat}', (req, res) => {
286+
// GET / => req.params = {}, splat is omitted
287+
// GET /foo/bar => req.params.splat = [ 'foo', 'bar' ]
288+
res.send('ok');
289+
});
290+
```
291+
292+
```ts
293+
import { type Request, type Response } from 'express';
294+
295+
// Matches / , /foo , /foo/bar , etc.
296+
app.get('/{*splat}', (req: Request, res: Response) => {
297+
// GET / => req.params = {}, splat is omitted
298+
// GET /foo/bar => req.params.splat = [ 'foo', 'bar' ]
299+
res.send('ok');
300+
});
301+
```
302+
303+
### Optional segments
304+
305+
Use braces to define optional segments in a route path. When the segment is not present, the parameter is omitted from `req.params`.
306+
307+
```js
308+
app.get('/:file{.:ext}', (req, res) => {
309+
// GET /image.png => req.params = { file: 'image', ext: 'png' }
310+
// GET /image => req.params = { file: 'image' }
311+
res.send('ok');
312+
});
313+
```
314+
315+
```ts
316+
import { type Request, type Response } from 'express';
317+
318+
app.get('/:file{.:ext}', (req: Request, res: Response) => {
319+
// GET /image.png => req.params = { file: 'image', ext: 'png' }
320+
// GET /image => req.params = { file: 'image' }
321+
res.send('ok');
322+
});
323+
```
324+
325+
The braces can also wrap a whole parameter to make it optional. Note that everything inside the braces is optional, so the position of the slash matters:
326+
327+
```js
328+
app.get('/user/{:id}', (req, res) => {
329+
// GET /user/42 => req.params = { id: '42' }
330+
// GET /user/ => req.params = {}
331+
// GET /user => 404, only the parameter is optional
332+
res.send('ok');
333+
});
334+
335+
app.get('/order{/:id}', (req, res) => {
336+
// GET /order/42 => req.params = { id: '42' }
337+
// GET /order => req.params = {}, the whole segment is optional
338+
res.send('ok');
339+
});
340+
```
341+
342+
```ts
343+
import { type Request, type Response } from 'express';
344+
345+
app.get('/user/{:id}', (req: Request, res: Response) => {
346+
// GET /user/42 => req.params = { id: '42' }
347+
// GET /user/ => req.params = {}
348+
// GET /user => 404, only the parameter is optional
349+
res.send('ok');
350+
});
351+
352+
app.get('/order{/:id}', (req: Request, res: Response) => {
353+
// GET /order/42 => req.params = { id: '42' }
354+
// GET /order => req.params = {}, the whole segment is optional
355+
res.send('ok');
356+
});
357+
```
358+
359+
Do not confuse the position of the slash in the route path with the [`strict routing` setting](/api/application/#application-settings), which is about the request URL: it controls whether a URL ending in a slash that the route path does not require still matches. For example, a request for `/order/` matches the `/order{/:id}` route by default, but returns a 404 error when strict routing is enabled; the trailing slash of `/user/` is unaffected because the `/user/{:id}` route requires it. All the requests commented in the examples above behave the same regardless of that setting.
360+
321361
## Route handlers
322362

323363
You can provide multiple callback functions that behave like [middleware](/guide/using-middleware) to handle a request. The only exception is that these callbacks might invoke `next('route')` to bypass the remaining route callbacks. You can use this mechanism to impose pre-conditions on a route, then pass control to subsequent routes if there's no reason to proceed with the current route.
@@ -514,7 +554,7 @@ The methods on the response object (`res`) in the following table can send a res
514554
## app.route()
515555

516556
You can create chainable route handlers for a route path by using `app.route()`.
517-
Because the path is specified at a single location, creating modular routes is helpful, as is reducing redundancy and typos. For more information about routes, see: [Router() documentation](/api/router).
557+
Because the path is specified in a single location, this helps to create modular routes and reduces redundancy and typos. For more information about routes, see the [Router() documentation](/api/router).
518558

519559
Here is an example of chained route handlers that are defined by using `app.route()`.
520560

@@ -647,7 +687,7 @@ app.use('/birds', birds);
647687

648688
The app will now be able to handle requests to `/birds` and `/birds/about`, as well as call the `timeLog` middleware function that is specific to the route.
649689

650-
But if the parent route `/birds` has path parameters, it will not be accessible by default from the sub-routes. To make it accessible, you will need to pass the `mergeParams` option to the Router constructor [reference](/api/application#appuse).
690+
But if the parent route `/birds` has path parameters, it will not be accessible by default from the sub-routes. To make it accessible, you will need to pass the `mergeParams` option to the [Router constructor](/api/express/#expressrouter).
651691

652692
```js
653693
const router = express.Router({ mergeParams: true });

0 commit comments

Comments
 (0)