A configuration based HTTPs gateway with a decent array of features written in TypeScript. Can replace Kong for basic purposes.
- APIs gateway
- The configuration is based on a TypeScript function called at runtime
- Match host and URL to upstream using strings or regular expressions
- Apply middleware by match
- Apply middleware globally
- Rate limitation middleware (uses redis)
- JWT verification middleware (rotating secrets, payload decoding and forwading)
- CORS headers middleware
- Remove headers middleware
- X-Request-ID creation and propagation middleware
- Basic Auth middleware
- Metadata middleware (can be used to fetch extra data for a user, organization or anything else specific about the request, uses redis)
- Authentication middleware
npm install donkey-gateway
If you wish to use Donkey directly, we recommend to use the example project
git clone git@github.com:batiste/donkey-boilerplate.git
cd donkey-boilerplate
docker compose up
You will need a redis server running on localhost
npm install
# on another process, you might need to run the test backend
node tests/backend.js
npm run serve
Or
docker compose up donkey-dev
Or
docker build . -t donkey-dev
docker run -p 3000:3000 donkey-dev
APIs configuration in Donkey works with a list of conditional IMatcher.
Each IMatcher is taken in order and the hosts
and uris
fields are tested with the current incoming request.
The IMatcher is used for this request if the provided fields match.
If no fields are present the IMatcher is always used.
import { createBasicAuthMiddleware } from './middlewares/basicAuth';
import { Config, IMatcher } from './schema';
export function getConfig(): Config {
const matchers: IMatcher[] = [
// match the HTTP header Host: loadtest
{
hosts: ['loadtest'],
upstream: 'localhost',
port: 8000,
timeout: 3
},
// match HTTP header Host: localhost and the /admin/ uri
{
hosts: ['localhost:3000'],
upstream: 'example.com',
// at least one uris should match. The match is done with startsWith
uris: ['/admin/'],
requestMiddlewares: [createBasicAuthMiddleware('admin', '1234')],
},
// match any leftover requests
{
upstream: 'example.com',
},
]
return { matchers }
}
export interface IMatcher {
/**
* Upstream domain, with no protocol and no path.
*/
upstream: string;
/**
* Port to use on the upstream. The default is 80.
*/
port?: number;
/**
* List of hosts to match.
*/
hosts?: (string | RegExp)[];
/**
* List of uris to match. String.startsWith is used for the match.
* A RegExp can also be provided. If stripeUri is true, the first capturing
* parenthesis will be used. If none present the whole match will be used.
*/
uris?: (string | RegExp)[];
/**
* Protocol to use on the upstream. The default is http.
*/
protocol?: "http:" | "https:";
/**
* Timeout when the gateway give up on the upstream. There is a default
* of 30 seconds.
*/
timeout?: number;
/**
* All middleware to apply on the client request or response.
*/
requestMiddlewares?: RequestMiddleware[];
/**
* All middleware to apply on the upstream response.
*/
responseMiddlewares?: ResponseMiddleware[];
/**
* Preserve the original host from the client. The default is false.
*/
preserveHost?: boolean;
/**
* Remove the matched path from the rest of the URI. E.g.
* If the matcher path is /admin/ and the incoming request is /admin/user/123
* the uri used on the upstream will become /user/123. As leading slash is enforced.
* The default is false.
*/
stripeUri?: boolean;
}
Load test with artillery, core usage ~15%
config:
target: "http://localhost:3000"
phases:
- duration: 60
arrivalRate: 100
scenarios:
- flow:
- get:
url: "/test"
headers:
host: loadtest
Backend with 0ms latency
Summary report @ 10:16:08(+0200) 2021-06-24
Scenarios launched: 6000
Scenarios completed: 6000
Requests completed: 6000
Mean response/sec: 99.16
Response time (msec):
min: 0
max: 52
median: 1
p95: 2
p99: 3
Scenario counts:
0: 6000 (100%)
Codes:
200: 6000
Backend with 200ms latency
Summary report @ 10:09:00(+0200) 2021-06-24
Scenarios launched: 6000
Scenarios completed: 6000
Requests completed: 6000
Mean response/sec: 99.16
Response time (msec):
min: 200
max: 256
median: 202
p95: 203
p99: 225
Scenario counts:
0: 6000 (100%)
Codes:
200: 6000