cache-me-ousside v0.1.0-alpha.3
cache-me-ousside
Your favorite Least Recently Used cache
A simple LRU cache that can be used as a proxy or reverse proxy, meaning that any request to this service will automatically be passed on to your specified REST API without the REST API having to change anything to accomodate the LRU cache.
As opposed to most LRU caches, cache-me-ousside
allows you to specify exactly which cache entries to bust and when. That means that one single POST
request will no longer clear your whole cache when it doesn't need to.
Contents
About
cache-me-ousside
is a server that will proxy all requests to any REST API and cache results of the configured routes in memory, so you can serve the results faster on the next request without having to do database queries or other expensive operations multiple times. In other words, you can cache your REST API resources outside of the API, so you don't have to integrate a cache on the API itself, making setup much easier.
cache-me-ousside
is a Least Recently Used cache, which means that when the cache is at capacity, the least recently accessed cache entries will be removed first (the FIFO principle). You can configure the cache capacity to be either a fixed number of entries or use a memory based limit (coming soon).
What makes this cache different from other LRU caches is that you can specify exactly which entries to remove when data on your API is updated. Do you have separate data, that in no way influence each other? Normally, an unsafe HTTP request to your API (such as POST or PUT) will remove all entries from your cache, but perhaps you only need POST requests that update your todos
to remove your cached todos
, so that you don't have to repopulate your cache with your posts
again. To configure the cache server to remove all entries on any unsafe HTTP request, see the Default LRU cache behavior section.
Getting started
It is super simple to get up and running with cache-me-ousside
!
Installing
To install the cache-me-ousside
binary, you have two options. Both of these allow you to run the cache-me-ousside
command from anywhere on your computer.
NPM
You will need to have NPM installed on your computer to use this command.
npm i -g cache-me-ousside
Go
You will need to have Go installed on your computer to use this command.
go install github.com/magnus-bb/cache-me-ousside@latest
GitHub
You can also download your platform specific executable from the GitHub releases page.
Uninstalling
Uninstalling cache-me-ousside
is as simple, as removing the cache-me-ousside
binary file, from your computer. How this is done depends on your method of installation.
Installed with NPM
Delete the cache-me-ousside(.exe)
binary located in your NPM bin
directory. You can find the bin
directory by running the following command in your terminal:
npm bin -g
You can also run the following script, that comes with this repo:
node scripts/postinstall.js uninstall
Installed with Go
Delete the file located at $GOPATH/bin/cache-me-ousside(.exe)
. The file extension will be exe
on a Windows machine. With bash, you can see the location of GOPATH
by running:
echo $GOPATH
Usage
There are three different ways of configuring cache-me-ousside
: a JSON5 file, environment variables, or command line flags. The minimal setup only requires you to specify a cache capacity, a proxy API URL, and a list of routes to cache for either HEAD or GET requests.
You can use the config.default.json5
file as a starting point for your configuration, you will just have to enter your own API URL in the file. This configuration will cache 500 entries, log output to the terminal, cache all GET and HEAD requests, and clear the whole cache when any unsafe HTTP method request is used, and run on http://localhost:8080. To not clear the whole cache on all unsafe HTTP method requests see the cache busting routes and patterns section.
See the configuration section for more details on all of the configuration options, or run the command:
cache-me-ousside --help
JSON5 configuration file
Using a JSON5 configuration file is the recommended method for configuring the cache. JSON5 is a superset of JSON, that allows you to write in a syntax that is closer to JavaScript. You can also use a regular JSON file.
Configuration
{
capacity: 500,
apiUrl: 'https://jsonplaceholder.typicode.com/',
cache: {
GET: [ '/posts', '/posts/:id' ],
},
}
Command line
cache-me-ousside --conf ./config.default.json5
Environment variables
Environment / .ENV
CAPACITY=500
API_URL=https://jsonplaceholder.typicode.com/
CACHE_GET=/posts,/posts/:id
Command line
cache-me-ousside
CLI flags
Command line
cache-me-ousside --cap 500 --url https://jsonplaceholder.typicode.com/ --cache:GET /posts,/posts/:id
Go package (coming soon)
It is possible to use this package to implement an LRU cache in your own project. You will get access to the data structures and methods neccesary for an LRU cache, with which you can do whatever you want.
Installation
go get github.com/magnus-bb/cache-me-ousside/cache
Usage
package main
import github.com/magnus-bb/cache-me-ousside/cache
func main() {
c := cache.New(500, "")
c.Set("key", "value")
c.Get("key")
}
See the documentation for more information on how to use the cache
package.
Configuration
cache-me-ousside
can be configured with JSON5/JSON, environment variables, or CLI flags. The three configuration methods can be used together (see the usage section for more details on how to use the configuration methods).
If you are mixing configuration methods, the order of precedence is as follows: 1. Command line flags 2. Environment variables 3. JSON5 configuration file
This means that if the same configuration option is specified more than once, the command line flag will be the one used if it is specified, otherwise the environment variable will be used if it is specified, otherwise the JSON5 configuration file will be used.
See the config.example.json5
file for reference on how to use the JSON5 configuration file.
Use cache-me-ousside --help
to see a list of all configuration options (flags and environment) in the terminal.
Configuration file path
Type: string
(path)
The file path that points to the JSON5 configuration file with options for the cache. This file can be used to specify all other configuration options than the configuration file path itself.
CLI flags
--config
| --conf
| --path
Example
cache-me-ousside --config ./config.default.json5
Environment variables
CONFIG_PATH
| CONFIG
Example
CONFIG_PATH=./config.default.json5
Cache capacity
Required
Type: uint64
Restrictions: Must be greater than 0
The cache capacity denotes how much data can be stored in the cache. The capacity can be either a fixed number of entries or a memory limit (coming soon). When the cache is full, the least recently accessed cache entry will be removed.
Regardless of whether you are setting a capacity of a specific number of entries or an amount of memory for the cache, the cache capacity should be set to a number (see the Cache capacity unit section for more details on the two modes).
CLI flags
--capacity
| --cap
Example
cache-me-ousside --config ./config.default.json5 --capacity 500
Environment variables
CAPACITY
Example
CAPACITY=500
JSON5 property
capacity
Example
{
// ...
capacity: 500,
// ...
}
Cache capacity unit (coming soon)
Type: string
Options: ""
| "b"
| "kb"
| "mb"
| "gb"
| "tb"
The cache capacity unit denotes which type of cache limit you want to impose. Leaving this option out or setting it to an empty string will default the cache capacity to use an entry-based cache limit, meaning that the cache capacity number will represent the exact number of entries that can be stored in the cache. If you set this option to one of the available units, the cache capacity limit will be set to the corresponding number of bytes.
CLI flags
--capacity-unit
| --cap-unit
| --cu
Example
cache-me-ousside --config ./config.default.json5 --capacity 500 --capacity-unit mb
Environment variables
CAPACITY_UNIT
Example
CAPACITY=500
CAPACITY_UNIT=mb
JSON5 property
capacityUnit
Example
{
// ...
capacity: 500,
capacityUnit: 'mb',
// ...
}
Cache server hostname
Type: string
Default: "localhost"
The cache server hostname is the hostname of the server that will be serving the cache. This is the first part of the server address (before port number) where the cache server can be accessed.
Be aware that the hostname does not include a scheme.
CLI flags
--hostname
| --hn
Example
cache-me-ousside --config ./config.default.json5 --hostname localhost
Environment variables
HOSTNAME
Example
HOSTNAME=localhost
JSON5 property
hostname
Example
{
// ...
hostname: 'localhost',
// ...
}
Cache server port number
Type: uint
Default: 8080
The cache server port number is the port number of the server that will be serving the cache. This is the second part of the server address (after hostname) where the cache server can be accessed.
CLI flags
--port
| -p
Example
cache-me-ousside --config ./config.default.json5 --port 8080
Environment variables
PORT
Example
PORT=8080
JSON5 property
port
Example
{
// ...
port: 8080,
// ...
}
REST API proxy URL
Required
Type: string
The REST API proxy URL is where all requests to the cache server will be proxied. That means, that any request sent to the cache server will be forwarded, exactly as-is, to the specified REST API (and responses from configured routes will be cached).
Trailing slashes are trimmed from the API URL so the same caching and busting configuration will work the same when you change the API URL from production to development etc. and perhaps omit the trailing slash in one or the other.
CLI flags
--api-url
| --url
| -u
Example
cache-me-ousside --config ./config.default.json5 --api-url https://jsonplaceholder.typicode.com/
Environment variables
API_URL
| PROXY_URL
Example
API_URL=https://jsonplaceholder.typicode.com/
JSON5 property
apiUrl
Example
{
// ...
apiUrl: 'https://jsonplaceholder.typicode.com/',
// ...
}
Limitations
For now, the API URL must point to a REST API. The cache works by storing cached entries with a key that is created from the HTTP method and route of the request, since these represent the operation and resource respectively (as opposed to e.g., GraphQL).
Log file path
Type: string
(path)
The log file path should point to a file into which all log messages (info, warnings, errors) will be written. If the log file path is omitted, the cache server will run in terminal mode instead, where all log messages will be printed to stdout
with some colorful formatting as well as icons.
CLI flags
--logfile
| --log
| -l
Example
cache-me-ousside --config ./config.default.json5 --logfile /path/to/logfile.log
Environment variables
LOGFILE_PATH
| LOGFILE
Example
LOGFILE_PATH=/path/to/logfile.log
JSON5 property
logFilePath
Example
{
// ...
logFilePath: '/path/to/logfile.log',
// ...
}
Cached routes
One variation required
Type: []string
Methods: GET | HEAD
The cached routes configurations denote which resources should be cached when the server matches an incoming request with the specific HTTP method and defined route(s). For now, it is only possible to cache GET and HEAD requests, which are the two variations of this configuration (denoted by <METHOD>
in the configuration examples). The cache server runs on Fiber, and as such follows the same route matching rules as the Fiber framework.
When setting cached routes with CLI flags, you can either choose to separate the routes to cache for every method with commas, or repeat the flag several times to add more routes to cache for every method. Using environment variables, you can separate routes with commas. We recommend using a JSON5 configuration file for simplicity, unless you wish to overwrite a file configuration option just once.
CLI flags
--cache:<METHOD>
| --c:<METHOD>
| --c:<METHOD_INITIAL>
Example
cache-me-ousside --config ./config.default.json5 --cache:GET /posts,/posts/:id --cache:HEAD /posts --c:h /posts/:id
Environment variables
CACHE_<METHOD>
Example
CACHE_GET=/posts,/posts/:id
CACHE_HEAD=/posts,/posts/:id
JSON5 property
cache.<METHOD>
Example
{
// ...
cache: {
GET: ['/posts', '/posts/:id'],
HEAD: ['/posts', '/posts/:id'],
}
// ...
}
Limitations
Currently, the cache server only supports caching GET and HEAD requests, but this might change in the future to allow for caching other types of API requests than REST.
It should be noted that some APIs distinguish between trailing slashes in routes (e.g., /posts
and /posts/
would have two different handlers), so this cache does as well to support these kinds of APIs. This means that you should strive to be consistent with your API requests in your application so you always either use trailing slashes or omit them in you app, so you avoid duplicating cache entries.
Cache busting routes and patterns
Type: []string
(CLI and env) or map[string][]string
(JSON5)
Methods: GET | HEAD | POST | PUT | DELETE | PATCH | TRACE | CONNECT | OPTIONS
The cache busting routes and patterns configuration is used to specify which request should remove (bust) specific entries in the cache. You can bust cache entries on any HTTP method, but we recommend only busting on unsafe HTTP methods. You can specify any HTTP method in the configuration by substituting <METHOD>
with the method name in the configuration examples. The cache server runs on Fiber, and as such follows the same route matching rules as the Fiber framework.
When setting bust routes and entry-matching patterns with CLI flags or environment variables, you must follow a specific syntax (which is why we recommend using the JSON5 configuration file when possible). Every bust configuration option for every method must specify ONE route to match (follows the Fiber syntax) and a list of regex patterns to use for matching entries in the cache to bust. The route and patterns must be separated by =>
, and the regex patterns must be separated by ||
. As with the cached routes configuration, you can either comma-separate every configuration to supply several route matches or repeat the flag several times to add more routes to bust (only in CLI). The separator characters are rather contrived, but designed to not conflict with the comma separator used by the cli-package, Fiber's route syntax, and regex. You must also enclose these options in double quotes when specified as a CLI flag to avoid shells such as bash from interpreting >
as a redirection operator.
All entry-busting patterns use regex syntax, but will first substitute route parameters specified with :
(just like Fiber's route matching syntax) with the corresponding values from the route. This means that a pattern like /posts/:id
will only remove an entry for /posts/1
if the matched route is /posts/1
. Compare this to the pattern /posts/
or /posts/.*
which would remove post entries with any ID.
All entries are saved in the cache in the format <METHOD>:<MATCHED_ROUTE>
. This means you can leverage the ^
(beginning of line) and $
(end of line) characters to specify whether you want to match a specific method or not and whether you want an exact match or anything containing the substring (see JSON5 examples).
CLI flags
--bust:<METHOD>
| --b:<METHOD>
| --b:<METHOD_INITIAL>
Example
cache-me-ousside --config ./config.default.json5 --bust:POST "/posts=>GET:/posts||HEAD:/posts,/posts/:id=>/posts/:id"
Environment variables
BUST_<METHOD>
Example
BUST_POST=/posts=>GET:/posts||HEAD:/posts,/posts/:id=>/posts/:id
BUST_PUT=/posts/:id=>/posts/:id
JSON5 property
bust.<METHOD>
Example
{
// ...
bust: {
POST: {
'/posts': [ // POST requests to /posts...
'GET:/posts', // ...will remove all GET entries that begin with /posts (that includes /posts/123 and so on)
'HEAD:/posts$', // ...will remove only the HEAD entry called /posts
],
'/posts/:id': [ // POST requests to /posts/:id...
'/posts/:id', // ...will remove both GET and HEAD entries that match the matched id only and all children route entries (e.g., /posts/123 and /posts/123/comments etc.)
],
},
PUT: {
// ...
},
DELETE: {
// ...
}
}
// ...
}
Default LRU cache behavior
If no bust routes and patterns are specified, the cache will never remove any entries, unless they expire (coming soon). If you want the standard LRU cache behavior, in which any unsafe HTTP request will clear the whole cache, you can specify all routes for the different HTTP methods as "*"
(wildcard) and an empty slice of patterns. The config.default.json5
file uses this behavior. Because of a bug with the JSON5 parsing Go package, you must use double quotes, when specifying the wildcard route ("*"
) - this should not be an issue, if you choose to use a regular JSON file.
Limitations
It should be noted that some APIs distinguish between trailing slashes in routes (e.g., /posts
and /posts/
would have two different handlers), so this cache does as well to support these kinds of APIs. This means that you should strive to be consistent with your API requests in your application so you always either use trailing slashes or omit them in you app, so you avoid missing your cache entries when you intended to bust them.
Roadmap
- GraphQL support (arbitrary routes + request body matching)
- Cache expiry
- Respect cache-related headers
- Public API of package
cache
- Allow for specifying GET and HEAD caching with one list of endpoints instead of two separate
Cache limitations
- You can only cache requests with GET and HEAD HTTP methods
- The proxied and cached API must be a REST API, since the cache server relies on the fact that routes denote the specific resource being requested, and that HTTP methods signify the kind of operation you are doing on the resource
- Changes to the resources on your API through any other channels than this cache server will not be reflected (bust entries) in the cache