Skip to content

Commit 00938e0

Browse files
authored
Merge pull request #2 from jkyberneees/support-gateway-cache
supporting gateway level cache
2 parents 169a3d5 + 64b87f2 commit 00938e0

File tree

4 files changed

+193
-7
lines changed

4 files changed

+193
-7
lines changed

README.md

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,126 @@ service.start(3000)
8080
}]
8181
}
8282
```
83+
## Gateway level caching
84+
### Why?
85+
> Because `caching` is the last mile for low latency distributed systems!
86+
87+
Enabling proper caching strategies at gateway level will drastically reduce the latency of your system,
88+
as it reduces network round-trips and remote services processing.
89+
We are talking here about improvements in response times from `X ms` to `~2ms`, as an example.
90+
91+
### Setting up gateway level cache available for all services
92+
#### Single node cache (memory):
93+
```js
94+
// cache middleware
95+
const cache = require('http-cache-middleware')()
96+
// enable http cache middleware
97+
const gateway = require('fast-gateway')
98+
const server = gateway({
99+
middlewares: [cache],
100+
routes: [...]
101+
})
102+
```
103+
> Memory storage is recommended if there is only one gateway instance and you are not afraid of losing cache data.
104+
105+
#### Multi nodes cache (redis):
106+
```js
107+
// redis setup
108+
const CacheManager = require('cache-manager')
109+
const redisStore = require('cache-manager-ioredis')
110+
const redisCache = CacheManager.caching({
111+
store: redisStore,
112+
db: 0,
113+
host: 'localhost',
114+
port: 6379,
115+
ttl: 30
116+
})
117+
118+
// cache middleware
119+
const cache = require('http-cache-middleware')({
120+
stores: [redisCache]
121+
})
122+
123+
// enable http cache middleware
124+
const gateway = require('fast-gateway')
125+
const server = gateway({
126+
middlewares: [cache],
127+
routes: [...]
128+
})
129+
```
130+
> Required if there are more than one gateway instances
131+
132+
### Enabling cache for service endpoints
133+
Although API Gateway level cache aims as a centralized cache for all services behind the wall, are the services
134+
the ones who indicate the responses to be cached and for how long.
135+
136+
Cache entries will be created for all remote responses coming with the `x-cache-timeout` header:
137+
```js
138+
res.setHeader('x-cache-timeout', '1 hour')
139+
```
140+
> Here we use the [`ms`](`https://www.npmjs.com/package/ms`) package to convert timeout to seconds. Please note that `millisecond` unit is not supported!
141+
142+
Example on remote service using `restana`:
143+
```js
144+
service.get('/numbers', (req, res) => {
145+
res.setHeader('x-cache-timeout', '1 hour')
146+
147+
res.send([
148+
1, 2, 3
149+
])
150+
})
151+
```
152+
153+
### Invalidating cache
154+
> Let's face it, gateway level cache invalidation was complex..., until now!
155+
156+
Remote services can also expire cache entries on demand, i.e: when the data state changes. Here we use the `x-cache-expire` header to indicate the gateway cache entries to expire using a matching pattern:
157+
```js
158+
res.setHeader('x-cache-expire', '*/numbers')
159+
```
160+
> Here we use the [`matcher`](`https://www.npmjs.com/package/matcher`) package for matching patterns evaluation.
161+
162+
Example on remote service using `restana`:
163+
```js
164+
service.patch('/numbers', (req, res) => {
165+
res.setHeader('x-cache-expire', '*/numbers')
166+
167+
// ...
168+
res.send(200)
169+
})
170+
```
171+
172+
### Custom cache keys
173+
Cache keys are generated using: `req.method + req.url`, however, for indexing/segmenting requirements it makes sense to allow cache keys extensions.
174+
Unfortunately, this feature can't be implemented at remote service level, because the gateway needs to know the entire lookup key when a request
175+
reaches the gateway.
176+
177+
For doing this, we simply recommend using middlewares on the service configuration:
178+
```js
179+
routes: [{
180+
prefix: '/users',
181+
target: 'http://localhost:3000',
182+
middlewares: [(req, res, next) => {
183+
req.cacheAppendKey = (req) => req.user.id // here cache key will be: req.method + req.url + req.user.id
184+
return next()
185+
}]
186+
}]
187+
```
188+
> In this example we also distinguish cache entries by `user.id`, very common case!
189+
190+
### Disable cache for custom endpoints
191+
You can also disable cache checks for certain requests programmatically:
192+
```js
193+
routes: [{
194+
prefix: '/users',
195+
target: 'http://localhost:3000',
196+
middlewares: [(req, res, next) => {
197+
req.cacheDisabled = true
198+
return next()
199+
}]
200+
}]
201+
```
202+
83203
## Want to contribute?
84204
This is your repo ;)
85205

package-lock.json

Lines changed: 68 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"homepage": "https://github.com/jkyberneees/fast-gateway#readme",
2828
"dependencies": {
2929
"fast-proxy": "^1.1.0",
30+
"http-cache-middleware": "^1.0.0",
3031
"restana": "^3.0.2"
3132
},
3233
"devDependencies": {

test/config.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ const pump = require('pump')
22

33
module.exports = async () => {
44
return {
5-
middlewares: [require('cors')()],
5+
middlewares: [
6+
require('cors')(),
7+
require('http-cache-middleware')()
8+
],
69

710
routes: [{
811
pathRegex: '',

0 commit comments

Comments
 (0)