Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate arity check in favor of explicit error handlers #59

Open
wants to merge 11 commits into
base: 2.0
Choose a base branch
from
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package-lock=false
13 changes: 2 additions & 11 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,8 @@
language: node_js
node_js:
- "0.10"
- "0.12"
- "1.8"
- "2.5"
- "3.3"
- "4.9"
- "5.12"
- "6.14"
- "7.10"
- "8.11"
- "9.11"
- "10.6"
- "12.14"
- "13.5"
sudo: false
cache:
directories:
Expand Down
15 changes: 15 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
unreleased
==========

* deps: mocha@7.0.0
* deps: supertest@4.0.2
* deps: nyc@15.0.0
* deps: setprototypeof@1.2.0
* deps: path-to-regexp@3.0.0
* deps: eslint@6.8.0
* deps: eslint-plugin-markdown@1.0.1
* Deprecate arity based error middleware
- Added `.error` instead of 4 argument functions
- Added deprecation warning when relying on arity based inference
- Added Stack & FlatStack

2.0.0-alpha.1 / 2018-07-27
==========================

Expand Down
143 changes: 127 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,14 +175,14 @@ rather than responding.

```js
router.route('/')
.all(function (req, res, next) {
next()
})
.all(check_something)
.get(function (req, res) {
res.setHeader('Content-Type', 'text/plain; charset=utf-8')
res.end('Hello World!')
})
.all(function (req, res, next) {
next()
})
.all(check_something)
.get(function (req, res) {
res.setHeader('Content-Type', 'text/plain; charset=utf-8')
res.end('Hello World!')
})
```

## Middleware
Expand All @@ -200,12 +200,35 @@ is returned from the function, the router will attach an `onRejected` callback
using `.then`. If the promise is rejected, `next` will be called with the
rejected value, or an error if the value is falsy.

### Error Middleware

Error middleware are special functions which take an `error` argument as the first
argument instead of the `request`. You can add error middleware by calling `.error()`.
These can be used for handling errors that occurred in previous handlers
(E.g. from calling `next(err)`). This is most used when you want to define shared
handling or rendering of errors.

```js
router.get('/error_route', (req, res, next) => {
next(new Error('Bad Request'))
})

router.error((err, req, res) => {
res.end(err.message) //=> "Bad Request"
})
```

Error handling middleware will **only** be invoked when an error was given. As
long as the error is in the pipeline, normal middleware and handlers will be
bypassed - only error handling middleware will be invoked with an error.

**Deprecated behavior:**

*Deprecated in `router@2.0.0`*

Middleware and method handlers can also be defined with four arguments. When
the function has four parameters defined, the first argument is an error and
subsequent arguments remain, becoming - "err", "req", "res", "next". These
functions are "error handling middleware", and can be used for handling
errors that occurred in previous handlers (E.g. from calling `next(err)`).
This is most used when you want to define arbitrary rendering of errors.
subsequent arguments remain, becoming - "err", "req", "res", "next".

```js
router.get('/error_route', function (req, res, next) {
Expand All @@ -217,10 +240,6 @@ router.use(function (err, req, res, next) {
})
```

Error handling middleware will **only** be invoked when an error was given. As
long as the error is in the pipeline, normal middleware and handlers will be
bypassed - only error handling middleware will be invoked with an error.

## Examples

```js
Expand Down Expand Up @@ -382,6 +401,98 @@ router.route('/pet/:id')
server.listen(8080)
```

## Migrating to 2.x from 1.x

#### Deprecation of arity based error middleware

In previous versions of `router`, the arity of a function was used to determine
if a middleware was a normal middleware or an error handling middleware. In `router@2.0`
this behavior still works, but is deprecated. The new api is as follows:
dougwilson marked this conversation as resolved.
Show resolved Hide resolved

```javascript
router.error(...errorMiddleware)

// Create a reusable stack of mw
const mw = new Router.Stack()

// Register normal middleware
mw.use(() => {}, () => {})

// Add an error middleware
mw.error((err, req, res) => {
// notice no more `next` required!
res.statusCode = 500
res.end(err.message)
})

// to explicitly opt back into the old behavior you can use `stack.infer`
mw.infer((req, res) => {
next(new Error())
}, (err, req, res, next) => {
// This sill be inferred from the arity like Express did in the past
// Notice that in this example all four arguments are required even
// if you never call next
})
```

### Update `path-to-regexp` to `3.x`

The update to `path-to-regexp@3.0.0` has a few breaking changes:

#### No longer a direct conversion to a RegExp with sugar on top.

It's a path matcher with named and unnamed matching groups. It's unlikely you previously abused this feature,
it's rare and you could always use a RegExp instead. An example of this would be:

```javascript
// Used to work
router.get('/\\d+')

// Now requires matching group
router.get('/(\\d+)')
```

#### All matching RegExp special characters can be used in a matching group.

Other RegExp features are not supported - no nested matching groups, non-capturing groups or look aheads
There is really only one common change is needing replacing any routes with `*` to `(.*)`. Some examples:

- `/:user(*)` becomes `/:user(.*)`
- `/:user/*` becomes `/:user/(.*)`
- `/foo/*/bar` becomes `/foo/(.*)/bar`

#### Parameters have suffixes that augment meaning - `*`, `+` and `?`. E.g. `/:user*`

Needs more info.

#### Named params with regex no longer define positionally.

One other small change (hopefully low impact), is that named parameters with regular expressions no longer result in positional
values in the `params` object. An example is:

```javascript
router.get('/:foo(.*)')

// old GET /bar
console.log(req.params) // {0: 'bar', 'foo': 'bar'}

// new GET /bar
console.log(req.params) // {'foo': 'bar'}
```

#### Partial matching, prefer escaping delimiter

The update to `path-to-regexp@3` includes a change to how partial matches are handled,
now you should escape the delimiter before a partial match segment. For example:

```javascript
// old
router.get('/user(s)?/:user/:op')

// new
router.get('\\/user(s)?/:user/:op')
```

## License

[MIT](LICENSE)
Expand Down
Loading