diff --git a/src/content/api/5x/api/request/index.mdx b/src/content/api/5x/api/request/index.mdx index 9960e91997..9518b49c4a 100644 --- a/src/content/api/5x/api/request/index.mdx +++ b/src/content/api/5x/api/request/index.mdx @@ -425,6 +425,8 @@ app.get('/files/*file', (req: Request, res: Response) => { }); ``` +Parameters defined in [optional segments](/guide/routing/#optional-segments) that are not present in the request URL are omitted from `req.params` entirely. + 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 nth capture group. ```js diff --git a/src/content/docs/en/4x/guide/routing.mdx b/src/content/docs/en/4x/guide/routing.mdx index 3924d9e7f9..f5ae694e44 100644 --- a/src/content/docs/en/4x/guide/routing.mdx +++ b/src/content/docs/en/4x/guide/routing.mdx @@ -108,7 +108,7 @@ app.all('/secret', (req: Request, res: Response, next: NextFunction) => { ## Route paths -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. +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. @@ -235,6 +235,26 @@ app.get('/ab(cd)?e', (req: Request, res: Response) => { }); ``` +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. + +```js +app.get('/file/*', (req, res) => { + // GET /file/javascripts/jquery.js + res.send(req.params[0]); + // => 'javascripts/jquery.js' +}); +``` + +```ts +import { type Request, type Response } from 'express'; + +app.get('/file/*', (req: Request, res: Response) => { + // GET /file/javascripts/jquery.js + res.send(req.params[0]); + // => 'javascripts/jquery.js' +}); +``` + ### Route paths based on regular expressions @@ -348,6 +368,14 @@ characters with an additional backslash, for example `\\d+`. 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 `*`. +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. + +``` +Route path: /file/*/size/* +Request URL: http://localhost:3000/file/javascripts/jquery.js/size/large +req.params: { "0": "javascripts/jquery.js", "1": "large" } +``` + ## Route handlers 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 ## app.route() You can create chainable route handlers for a route path by using `app.route()`. -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). +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). Here is an example of chained route handlers that are defined by using `app.route()`. @@ -677,7 +705,7 @@ app.use('/birds', birds); 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. -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). +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). ```js const router = express.Router({ mergeParams: true }); diff --git a/src/content/docs/en/5x/guide/routing.mdx b/src/content/docs/en/5x/guide/routing.mdx index cc05cefe3e..ca4da2e2ff 100644 --- a/src/content/docs/en/5x/guide/routing.mdx +++ b/src/content/docs/en/5x/guide/routing.mdx @@ -108,7 +108,7 @@ app.all('/secret', (req: Request, res: Response, next: NextFunction) => { ## Route paths -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. +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. @@ -153,77 +153,9 @@ app.get('/random.text', (req: Request, res: Response) => { }); ``` -### Wildcards - -Wildcards match any path after a prefix. They must have a name, just like route parameters, and are captured as arrays of path segments. - -```js -app.get('/files/*filepath', (req, res) => { - // GET /files/images/logo.png - console.dir(req.params.filepath); - // => [ 'images', 'logo.png' ] - res.send(`File: ${req.params.filepath.join('/')}`); -}); -``` - -```ts -import { type Request, type Response } from 'express'; - -app.get('/files/*filepath', (req: Request<{ filepath: string[] }>, res: Response) => { - // GET /files/images/logo.png - console.dir(req.params.filepath); - // => [ 'images', 'logo.png' ] - res.send(`File: ${req.params.filepath.join('/')}`); -}); -``` - -To also match the root path, wrap the wildcard in braces: - -```js -// Matches / , /foo , /foo/bar , etc. -app.get('/{*splat}', (req, res) => { - // GET / => req.params.splat = [] - // GET /foo/bar => req.params.splat = [ 'foo', 'bar' ] - res.send('ok'); -}); -``` - -```ts -import { type Request, type Response } from 'express'; - -// Matches / , /foo , /foo/bar , etc. -app.get('/{*splat}', (req: Request, res: Response) => { - // GET / => req.params.splat = [] - // GET /foo/bar => req.params.splat = [ 'foo', 'bar' ] - res.send('ok'); -}); -``` - -### Optional segments - -Use braces to define optional segments in a route path. When the segment is not present, the parameter is omitted from `req.params`. - -```js -app.get('/:file{.:ext}', (req, res) => { - // GET /image.png => req.params = { file: 'image', ext: 'png' } - // GET /image => req.params = { file: 'image' } - res.send('ok'); -}); -``` - -```ts -import { type Request, type Response } from 'express'; - -app.get('/:file{.:ext}', (req: Request, res: Response) => { - // GET /image.png => req.params = { file: 'image', ext: 'png' } - // GET /image => req.params = { file: 'image' } - res.send('ok'); -}); -``` - -The characters `?`, `+`, `*`, `[]`, and `()` are reserved and cannot be used as literal characters in route paths. Use `\` to escape them if needed. +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. @@ -259,7 +191,11 @@ app.get(/.*fly$/, (req: Request, res: Response) => { ## Route parameters -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. +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. + +### Named parameters + +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. ``` Route path: /users/:userId/books/:bookId @@ -293,7 +229,7 @@ app.get('/users/:userId/books/:bookId', sendParams); -The name of route parameters must be made up of "word characters" ([A-Za-z0-9_]). +The name of route parameters must be a valid JavaScript identifier. Other names can be used by quoting them, for example `:"user-name"`. @@ -313,11 +249,115 @@ req.params: { "genus": "Prunus", "species": "persica" } -Regexp characters are not supported in route paths. Use an array of paths or regular expressions instead. +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. See the [path route matching syntax](/guide/migrating-5#path-route-matching-syntax) for more information. +### Wildcards + +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. + +```js +app.get('/files/*filepath', (req, res) => { + // GET /files/images/logo.png + console.dir(req.params.filepath); + // => [ 'images', 'logo.png' ] + res.send(`File: ${req.params.filepath.join('/')}`); +}); +``` + +```ts +import { type Request, type Response } from 'express'; + +app.get('/files/*filepath', (req: Request<{ filepath: string[] }>, res: Response) => { + // GET /files/images/logo.png + console.dir(req.params.filepath); + // => [ 'images', 'logo.png' ] + res.send(`File: ${req.params.filepath.join('/')}`); +}); +``` + +To also match the root path, wrap the wildcard in braces: + +```js +// Matches / , /foo , /foo/bar , etc. +app.get('/{*splat}', (req, res) => { + // GET / => req.params = {}, splat is omitted + // GET /foo/bar => req.params.splat = [ 'foo', 'bar' ] + res.send('ok'); +}); +``` + +```ts +import { type Request, type Response } from 'express'; + +// Matches / , /foo , /foo/bar , etc. +app.get('/{*splat}', (req: Request, res: Response) => { + // GET / => req.params = {}, splat is omitted + // GET /foo/bar => req.params.splat = [ 'foo', 'bar' ] + res.send('ok'); +}); +``` + +### Optional segments + +Use braces to define optional segments in a route path. When the segment is not present, the parameter is omitted from `req.params`. + +```js +app.get('/:file{.:ext}', (req, res) => { + // GET /image.png => req.params = { file: 'image', ext: 'png' } + // GET /image => req.params = { file: 'image' } + res.send('ok'); +}); +``` + +```ts +import { type Request, type Response } from 'express'; + +app.get('/:file{.:ext}', (req: Request, res: Response) => { + // GET /image.png => req.params = { file: 'image', ext: 'png' } + // GET /image => req.params = { file: 'image' } + res.send('ok'); +}); +``` + +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: + +```js +app.get('/user/{:id}', (req, res) => { + // GET /user/42 => req.params = { id: '42' } + // GET /user/ => req.params = {} + // GET /user => 404, only the parameter is optional + res.send('ok'); +}); + +app.get('/order{/:id}', (req, res) => { + // GET /order/42 => req.params = { id: '42' } + // GET /order => req.params = {}, the whole segment is optional + res.send('ok'); +}); +``` + +```ts +import { type Request, type Response } from 'express'; + +app.get('/user/{:id}', (req: Request, res: Response) => { + // GET /user/42 => req.params = { id: '42' } + // GET /user/ => req.params = {} + // GET /user => 404, only the parameter is optional + res.send('ok'); +}); + +app.get('/order{/:id}', (req: Request, res: Response) => { + // GET /order/42 => req.params = { id: '42' } + // GET /order => req.params = {}, the whole segment is optional + res.send('ok'); +}); +``` + +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. + ## Route handlers 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 ## app.route() You can create chainable route handlers for a route path by using `app.route()`. -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). +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). Here is an example of chained route handlers that are defined by using `app.route()`. @@ -647,7 +687,7 @@ app.use('/birds', birds); 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. -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). +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). ```js const router = express.Router({ mergeParams: true });