mokd v2.0.1
mokd
Programmable fake APIs server.
- Requirements
- Installation
- Usage
- Endpoint Configuration
- Params interceptors
- Response transformers
-
transformJSON-transformText - Development Mode
- Contributing
- Credits
- License
Requirements
Installation
npm install mokd --saveUsage
const { Server } = require('mokd');
const server = new Server({
baseUrl: '', //optional
endpoints: [
{
path: '/user/',
response: {
id: 1
name: 'John Doe'
}
//... more endpoints configuration here, see below
}
]
});
const { data } = server.resolve({ url: '/user/' });
//data === { "id": 1, "name": "John Doe" }Usage as an express/connect middleware
In order to use the server with an existing connect/express application you need to use the built-in adapter.
const express = require('express');
const { Server, connectServer } = require('mokd');
const server = new Server({
baseUrl: '', //optional
endpoints: [
//... endpoints configuration object here, see below
]
});
const app = express();
app.use(connectServer(server));
app.listen(8000);Usage as a koa middleware
In order to use the server with an existing koa application you need to use the built-in adapter.
const Koa = require('koa');
const { Server, koaServer } = require('mokd');
const server = new Server({
baseUrl: '', //optional
endpoints: [
//... endpoints configuration object here, see below
]
});
const app = new Koa();
app.use(koaServer(server));
app.listen(8000);Create your own adapter
If you're using another application framework, you can create a custom adapter. Refer to the built-in koa or connect for example implementations.
Endpoint Configuration
The core of the server is the endpoints configuration option.
You can provide either an array of endpoints or a function returning an array of endpoints. In the latter case the function receives the server instance as first argument.
const { Server } = require('mokd');
//endpoints as array
const server = new Server({
endpoints: [
//... endpoints configuration object here, see below
]
});
const otherServer = new Server({
endpoints: (srv) => [
// you can access instance configuration here via `srv.options`
//... endpoints configuration object here, see below
]
});Endpoint properties
Endpoints are objects with the following properties:
method(string): The expected methods of the incoming request (default:GET),path(string|regexp|function): the path to match relative to the root URL. If a function it must return a string or a regular expression (default:null),delay(number|function): force a delay in milliseconds for the response. If a function it must return a number (default:0),contentType(string): Response content type (default:application/json),response(*|function): Response body template. Could be any type of content in relation to theContentTypeparameter. If a function it will be executed with aparamsobject and theendpointitself as arguments. (default:null)
The params object
The params object contains 3 properties:
$req: the original request object$parsedUrl: The request URL parsed by NodeJS nativeurl.parsemethod$routeMatch: either an object (whenpathis a string) or an array of matched segments (whenpathis a regular expression). See below for details.
Path Matching Formats and $routeMatch
Endpoint's path configuration could be a plain string, a regular expression or a string with Express-like parameters (see path-to-regexp for details).
$routeMatch format will vary based on the provided path:
- regular expression:
$routeMatchwill be the resulting array of callingRegExp.prototype.execon it - string or Express-like route:
$routeMatchwill be and object with named parameters as keys. Note that even numeric parameters will be strings.
Examples:
/* Plain string */
{
path: '/api/v1/users'
// /api/v1/users/ -> $routeMatch === {}
}
/* Express-like path */
{
path: '/api/v1/users/:id'
// /api/v1/users/10 -> $routeMatch === {id: '10'}
}
/* RegExp */
{
path: /^\/api\/v1\/users\/(\d+)$/
// /api/v1/users/10 -> $routeMatch === ['/api/v1/users/10', '10']
}Endpoint response template
Any key in the response template could be either a plain value or a function. If a function, it will be executed at response time with a params object and the endpoint itself as arguments.
Endpoint Base URL
The baseUrl configuration option sets up a base URL for every relative endpoint path provided. To override the base URL use absolute URLs.
Note: baseUrl applies just to string paths.
const server = new Server({
baseUrl: '/api/v1/', //optional
endpoints: [
{
// this endpoint will respond at /api/v1/users
path: 'users',
response: {
// ...
}
}, {
// this endpoint will respond at /custom/path
path: '/custom/path',
response: {
// ...
}
}
]
});Examples
1) A basic GET endpoint returning a JSON object
const endpoint = {
path: '/api/v1/user',
response: {
name: 'John',
surname: 'Doe'
}
};2) A GET endpoint returning dynamic data provided by Chance
const chance = require('chance').Chance();
const endpoint = {
path: '/api/v1/user',
response: {
name: () => chance.first(),
surname: () => chance.last()
}
};3) A GET endpoint matching a regexp and returning a dynamic property based on the match
const chance = require('chance').Chance();
const endpoint = {
//matches either a male of female user request
path: /\/api\/v1\/user\/(male|female)$/,
response: {
name: (params) => chance.first({
gender: params.$routeMatch[1]
}),
surname: (params) => chance.last({
gender: params.$routeMatch[1]
})
}
};4) A GET endpoint matching a regexp and returning a dynamic response based on the match
const chance = require('chance').Chance();
const endpoint = {
path: '/api/v1/:frag',
response: (params) => {
//calling /api/v1/user
//params.$routeMatch === {'frag': 'user'}
if (params.$routeMatch.frag === 'user') {
return {
name: () => chance.first(),
surname: () => chance.last()
};
}
return {
error: 'Not matching anything'
};
}
};5) A POST endpoint, accepting a body request and returning a success message
Note: to parse the request body you usually need to enable body parsing in your application (in express / connect you can use body-parser).
const endpoint = {
path: '/api/v1/send',
method: 'POST',
response: (params) => {
if (!params.$req.body.username || !params.$req.body.password) {
return {
success: false,
msg: 'You must provide a username and a password'
};
}
return {
success: true,
msg: 'Succesfully logged in!'
};
}
};Params interceptors
The params object is automatically generated by the server. Anyway you can manipulate it by providing an array of interceptor functions as the interceptors key of the server configuration.
Every interceptor function receives the params object filtered by the previous interceptor.
const addCustomKey = (params) => Object.assign(params, { keyID: 'my-custom-id' });
const addIfKey = (params) => Object.assign(params, params.keyID && { someData: '...' });
const server = new Server({
interceptors: [
addCustomKey,
addIfKey
]
});Response transformers
You can instruct the server on which data format it has to provide for each endpoint's contentType with response transformers.
Response transformers are functions that take in 3 arguments and return formatted data (usually stringified in order the be a valid server response).
The arguments are:
data: raw data object generated by theresponseproperty of the endpoint. Note that at this point dynamic response fields have not been yet been processedendpoint: matched endpoint for the requestparams: param object as used in theresponseproperty of the endpoint
An array of transform functions can be set as a transformers key in the Server configuration. The server will iterate on every transformer until it encounters one that doesn't return undefined.
By default the server comes with two built-in transformers:
transformJSON
Resolves any dynamic response template key and returns a stringified JSON object.
It's an high order function that takes a JSON stringifier function as first argument (defaults to JSON.stringify).
const { Server } = require('mokd');
const { transformJSON } = require('mokd/lib/utils');
cost server = new Server({
endpoints: [ ... ],
transformers: [
transformJSON()
]
});transformText
Returns a string representation of the data provided by the endpoint's response.
const { Server } = require('mokd');
const { transformText } = require('mokd/lib/utils');
cost server = new Server({
endpoints: [ ... ],
transformers: [
transformText
]
});Development Mode
Mocked data could change frequently during development. Instead or restarting your application, you can instantiate a watcher that will listen for file changes and reload endpoints automatically.
In order for the watcher to work correctly you need to move the endpoints configuration to its own file and pass its path to the watcher.
// endpoints.js
module.exports = [{
path: 'api/users/'
response {
// ...
}
}];
// server.js
const { createWatcher, Server } = require('../index');
const server = new Server(); // <-- don't pass endpoints here
const watcher = createWatcher({
server,
entrypoint: './endpoints.js'
paths: ['./mocks/**/*.*'] //additional paths to watch
});
watcher.start();Options
createWatcher config object as the following options:
server: A mock server instancecwd: (default:process.cwd()) The base directory from which relative paths are resolved.entrypoint: A path to a file exposing a list of endpoints. Either absolute or relative tocwdpaths: A list of files or patterns to be watched for changes (Seechokidar.watchpathargument for details).watchOptions:chokidar.watchoptions. By defaultignoreInitialistrueandcwdhas the same value as thecwdoption here.
Methods
createWatcher returned object has the following methods:
start: runs the watcher.close: proxy to chockidar'sclosemethodon: proxy to chockidar'sonmethodupdate(clear = true): Loads a fresh copy of theentrypointfile and every watched file. To reload just the entrypoint setcleartofalse.clearCache: removes watched files from NodeJS module's cache.
Contributing
- Fork it or clone the repo
- Install dependencies
npm install - Run
npm startto launch a development server - Code your changes and write new tests in the
testsfolder. - Ensure everything is fine by running
npm testandnpm run eslint - Push it or submit a pull request :D
Credits
Created by Marco Solazzi