Skip to content

Commit de41864

Browse files
committed
initial commit 1.0.0
1 parent 92a090e commit de41864

File tree

7 files changed

+3706
-1
lines changed

7 files changed

+3706
-1
lines changed

README.md

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,107 @@
11
# middleware-if-unless
2-
Invokes connect-like middleware if / unless routing criteria matches. Inspired on express-unless module.
2+
Invokes connect-like middleware if / unless routing criteria matches. Inspired on [express-unless](https://www.npmjs.com/package/express-unless) module.
3+
4+
## Main features
5+
- Advanced routes matching capabilities. Uses [find-my-way](https://www.npmjs.com/package/find-my-way) or any compatible router to match the routes.
6+
- `iff`: execute middleware only if the routes matches. Ideal use case: API gateways (see: [k-fastify-gateway](https://www.npmjs.com/package/k-fastify-gateway))
7+
- `unless`: execute middleware always unless the routes matches.
8+
- Arbitraty chaining of iff -> unless of vice-versa.
9+
- Low overhead, crazy fast implementation.
10+
11+
12+
# Usage example
13+
How to extend any connect-like middleware:
14+
```js
15+
const iu = require('middleware-if-unless')()
16+
17+
const middleware = function (req, res, next) {
18+
res.body = 'hit'
19+
20+
return next()
21+
}
22+
23+
// extend middleware with iff/unless capabilities
24+
iu(middleware)
25+
```
26+
## unless
27+
Execute middleware unless routing restrictions matches:
28+
```js
29+
const app = require('express')()
30+
app.use(middleware.unless([
31+
'/not/allowed/to/hit'
32+
]))
33+
34+
...
35+
```
36+
In this example, all requests except `[GET] /not/allowed/to/hit` will cause the middleware to be executed.
37+
38+
## if
39+
Execute middleware only if routing restrictions matches:
40+
```js
41+
const app = require('express')()
42+
app.use(middleware.iff([
43+
{
44+
methods: ['POST', 'DELETE', 'PUT', 'PATCH'],
45+
url: '/tasks/:id'
46+
}
47+
]))
48+
49+
...
50+
```
51+
### Chaining
52+
You can optionally chain iff -> unless or vice-versa:
53+
```js
54+
app.use(middleware
55+
.iff(req => req.url.startsWith('/pets')) // 4 check
56+
.iff([ // 3 check
57+
'/pets/*',
58+
'/pets/:id/*'
59+
]).unless([ // 2 check
60+
'/pets/:id/owners',
61+
{
62+
url: '/pets/:id', methods: ['DELETE']
63+
}
64+
]).unless(req => req.url.endsWith('.js')) // 1 check
65+
)
66+
```
67+
# Configuration
68+
## module
69+
```js
70+
const iu = require('middleware-if-unless')(
71+
// optional router configuration:
72+
// https://www.npmjs.com/package/find-my-way#findmywayoptions
73+
{
74+
}
75+
,
76+
// optional router factory:
77+
// allows to override find-my-way as default router
78+
function(opts){}
79+
)
80+
```
81+
## iff / unless
82+
Both methods share the same configuration format:
83+
84+
### - routing criteria is a function
85+
```js
86+
middleware.iff(req => req.url.startsWith('/pets'))
87+
```
88+
### - routing criteria is an array of routes
89+
```js
90+
middleware.iff([
91+
'/login', // if string is passed, the GET method is inferred
92+
{
93+
methods: ['DELETE', 'POST', '...'],
94+
url: '/task/:id/*'
95+
}
96+
])
97+
```
98+
### - routing criteria is an object
99+
```js
100+
middleware.unless({ endpoints: [
101+
'/login', // if string is passed, the GET method is inferred
102+
{
103+
methods: ['DELETE', 'POST', '...'],
104+
url: '/task/:id/*'
105+
}
106+
]})
107+
```

index.js

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
const op = () => true
2+
3+
module.exports = function (routerOpts = {}, routerFactory) {
4+
routerOpts.defaultRoute = () => false
5+
routerFactory = routerFactory || require('find-my-way')
6+
7+
function exec (options, isIff = true) {
8+
const middleware = this
9+
10+
// independent router instance per config
11+
const router = routerFactory(routerOpts)
12+
13+
const opts = typeof options === 'function' ? { custom: options } : (Array.isArray(options) ? { endpoints: options } : options)
14+
if (opts.endpoints && opts.endpoints.length) {
15+
// setup matching router
16+
opts.endpoints.map(endpoint => typeof endpoint === 'string' ? { methods: ['GET'], url: endpoint } : endpoint).forEach(({ methods, url }) => {
17+
router.on(methods, url, op)
18+
})
19+
}
20+
21+
const result = function (req, res, next) {
22+
// supporting custom matching function
23+
if (opts.custom) {
24+
if (opts.custom(req)) {
25+
if (isIff) {
26+
return middleware(req, res, next)
27+
}
28+
} else if (!isIff) {
29+
return middleware(req, res, next)
30+
}
31+
32+
// leave here and do not process opts.endpoints
33+
return next()
34+
}
35+
36+
// matching endpoints and moving forward
37+
if (router.lookup(req, res)) {
38+
if (isIff) {
39+
return middleware(req, res, next)
40+
}
41+
} else if (!isIff) {
42+
return middleware(req, res, next)
43+
}
44+
45+
return next()
46+
}
47+
48+
// allowing chaining
49+
result.iff = iff
50+
result.unless = unless
51+
52+
return result
53+
}
54+
55+
function iff (options) {
56+
return exec.call(this, options, true)
57+
}
58+
function unless (options) {
59+
return exec.call(this, options, false)
60+
}
61+
62+
return function (middleware) {
63+
middleware.iff = iff
64+
middleware.unless = unless
65+
66+
return middleware
67+
}
68+
}

0 commit comments

Comments
 (0)