Build a fake backend by providing the content of files or JavaScript objects through configurable routes. This service allows the developer to work on a new feature or an existing one using fake data while the real service is in development.
Start by adding the service as a development dependency.
yarn add --dev the-fake-backend
or
npm i --save-dev the-fake-backend
After installing, create a new file that will be responsible for configuring and starting the service.
const { createServer } = require('the-fake-backend');
const server = createServer();
server.routes([
{
path: '/example',
methods: [
{
type: 'get', // or MethodType.GET with Typescript
data: 'your-response-data-here',
// data: (req) => 'your-response-data-here-based-in-request'
},
],
},
]);
server.listen(8080);
This will create the http://localhost:8080/example endpoint.
You can also use files content as response instead of using the data
property.
const { createServer } = require('the-fake-backend');
const server = createServer();
server.routes([
{
path: '/cats',
methods: [
{
type: 'get', // or MethodType.GET with Typescript
},
],
},
{
path: '/dogs',
methods: [
{
type: 'get', // or MethodType.GET with Typescript
file: 'data/my/custom/path/to/dogs.txt',
// file: req => `data/my/custom/path/to/dogs-${req.query.type}.txt`
},
],
},
]);
server.listen(8080);
The script above generates the following two endpoints.
Method | Path | Response |
---|---|---|
GET | http://localhost:8080/cats | The data/cats.json file content |
GET | http://localhost:8080/dogs | The data/my/custom/path/to/dogs.txt file content |
Property | Required | Description |
---|---|---|
basePath | no | An API context prefix (e.g. /v1 ) |
middlewares | no | An array of express's middlewares |
proxies | no | The server proxies |
throttlings | no | The server throttlings |
pagination | no | The server pagination setup |
docsRoute | no | The route that will print all the routes as HTML |
definitions | no | The GraphQL definitions. |
This property allows to proxy requests.
Property | Required | Default | Description |
---|---|---|---|
proxies[].name | yes | The proxy name | |
proxies[].host | yes | The proxy host (e.g.: http://api.dev.com/api ) |
|
proxies[].appendBasePath | No | false |
Whether basePath should be appended in target |
proxies[].onProxyReq | No | A proxy request handler to forward to http-proxy-middleware | |
proxies[].onProxyRes | No | A proxy response handler to forward to http-proxy-middleware |
This property allows responses to be throttled.
Property | Required | Description |
---|---|---|
throttlings[].name | yes | Custom throttling name |
throttlings[].values | yes | Custom throttling range (array of numbers, in ms) |
This property allows routes to be paginated. Response attributes may be printed in response payload (wrapping the given fixture) or headers. Request parameters are read from URL query string.
Property | Required | Default | Type | Description |
---|---|---|---|---|
count | no | 'count' |
Response attribute | Current page items count |
data | no | 'data' |
Response attribute | Current page data |
empty | no | 'empty' |
Response attribute | Whether if current page is empty |
first | no | 'first' |
Response attribute | Whether if current page is the first one |
headers | no | false |
Configuration | Whether response attributes will be present in headers |
last | no | 'last' |
Response attribute | Whether if current page is the last one |
next | no | 'next' |
Response attribute | Whether if there is a next page |
offsetParameter | no | 'offset' |
Request parameter | Requested offset |
page | no | 'page' |
Response attribute | Current page |
pageParameter | no | 'page' |
Request parameter | Requested page |
pages | no | 'pages' |
Response attribute | Pages count |
sizeParameter | no | 'size' |
Request parameter | Requested page size |
total | no | 'total' |
Response attribute | Total items count |
Property | Required | Description |
---|---|---|
routes[].path | yes | The endpoint address (URI). |
routes[].methods | yes | The route methods, check the method's properties table below. |
Property | Type | Required | Default | Description |
---|---|---|---|---|
methods[].type | string | yes | HTTP request type | |
methods[].code | number | no | 200 |
HTTP response status code |
methods[].data | any | (req) => any | no | HTTP response data. May also be a function with request | |
methods[].file | string | (req) => string | no | /data/${req.path} |
HTTP response data fixture file (when data is not given). May also be a function with request |
methods[].headers | object | (req) => object | no | HTTP response headers. May also be a function with request | |
methods[].delay | number | no | HTTP response delay/timeout, in milliseconds | |
methods[].search | object | no | Search parameters | |
methods[].pagination | boolean | object | no | false |
Whether data is paginated or not. May also be a pagination object |
methods[].overrides | object[] | no | Custom response scenarios (switchable in CLI) | |
methods[].overrideContent | (req, content) => any | no | A function to override response content before send |
This property allows routes to be searchable.
Property | Type | Required | Default | Description |
---|---|---|---|---|
parameter | string | yes | 'search' |
Query string parameter name |
properties | string[] | yes | An array of properties to apply the search |
This property allows you to create an array of options that will override the current method
option.
Property | Type | Required | Default | Description |
---|---|---|---|---|
overrides[].name | string | yes | Scenario name | |
overrides[].code | number | no | 200 |
Described above |
overrides[].data | any | (req) => any | no | Described above | |
overrides[].file | string | (req) => string | no | Described above | |
overrides[].headers | object | (req) => object | no | Described above | |
overrides[].delay | number | no | Described above | |
overrides[].search | object | no | Described above | |
overrides[].pagination | boolean | object | no | false |
Described above |
overrides[].overrideContent | (req, content) => any | no | Described above | |
overrides[].selected | boolean | no | false |
Described above |
We're using apollo-server-express to integrate the GraphQL Server to the the-fake-backend
. When you have the definitions
property, the server will enable the GraphQL's related endpoints.
Method | Path | Response |
---|---|---|
GET | http://localhost:8080/graphql | The graphical interactive in-browser GraphQL IDE |
POST | http://localhost:8080/graphql | The queries and mutations response |
The service searches for a JSON file inside the graphl/queries/
folder using the query name, for example, the query below tries to respond with the graphl/queries/getPerson.json
file's content.
const { createServer } = require('the-fake-backend');
const serverOptions = {
definitions: `
type Person {
id: String
name: String
age: Int
}
type Query {
getPerson(id: String): Person
}
`,
};
const server = createServer(serverOptions);
server.listen(8080);
The same happens to the mutations, for example, the mutation below tries to respond with the graphl/mutations/createPerson.json
file's content.
const { createServer } = require('the-fake-backend');
const serverOptions = {
definitions: `
type Person {
id: String
name: String
age: Int
}
input PersonInput {
name: String
age: Int
}
type Mutation {
createPerson(person: PersonInput): Person
}
`,
};
const server = createServer(serverOptions);
server.listen(8080);
The GraphQL's endpoints does not have support for throttlings, proxies, pagination, overridings and search at the moment, we are still working on these features.
When a request is made the server will check if the method
object contains the overrides
property and if there is one override
selected through the property selected
. If there is an override
selected it will be merged with the method
object.
const server = createServer({ ... })
server.routes([
{
path: '/user',
methods: [
{
type: 'get', // or MethodType.GET with Typescript
file: 'data/my/custom/path/to/client-user.json',
overrides: [
{
name: 'Staff',
file: 'data/my/custom/path/to/staff-user.json'
},
{
name: 'Super Admin',
file: 'data/my/custom/path/to/super-admin-user.json'
},
{
name: 'Error 500',
code: 500
}
]
}
]
}
]);
// curl -XGET http://localhost:8080/user
// Returns `data/my/custom/path/to/client-user.json` file content.
Press 'o' on terminal and change the URL '/user' with method 'get' with override 'Super Admin'
// curl -XGET http://localhost:8080/user
// Returns `data/my/custom/path/to/super-admin-user.json` file content.
You can override the response content after all the processing (file/data content, pagination, search, etc.).
// /data/dogs.json
[
{ "id": 1, "name": "Doogo" },
{ "id": 2, "name": "Dogger" },
{ "id": 3, "name": "Dog" },
{ "id": 4, "name": "Doggernaut" },
{ "id": 5, "name": "Dogging" }
]
const { createServer } = require('the-fake-backend');
const server = createServer();
server.routes([
{
path: '/dogs',
methods: [
{
type: 'get', // or MethodType.GET with Typescript
overrideContent: (req, content) => ({
...content,
{ id: 6, name: 'Bulldog' }
})
},
],
},
]);
server.listen(8080);
You can make an endpoint searchable by declaring the search property in a route.
// /data/dogs.json
[
{ "id": 1, "name": "Doogo" },
{ "id": 2, "name": "Dogger" },
{ "id": 3, "name": "Dog" },
{ "id": 4, "name": "Doggernaut" },
{ "id": 5, "name": "Dogging" }
]
const { createServer } = require('the-fake-backend');
const server = createServer();
server.routes([
{
path: '/dogs',
methods: [
{
type: 'get', // or MethodType.GET with Typescript
search: {
parameter: 'search',
properties: ['name'],
},
},
],
},
]);
server.listen(8080);
You can now make requests to the http://localhost:8080/dogs?search=dogg
endpoint. The response will be the data/dogs.json
file content filtered.
[
{ "id": 2, "name": "Dogger" },
{ "id": 4, "name": "Doggernaut" },
{ "id": 5, "name": "Dogging" }
]
You can make an endpoint paginated by declaring the pagination options in the server (just in case of overriding default values), adding pagination parameter in a route and visiting it with pagination query string parameters.
Route pagination parameter may be a boolean (true) to use global pagination options, or pagination object parts to override global ones.
Note: The pagination is zero-based, so 0 is the first page.
// /data/dogs.json
[
{ "id": 1, "name": "Doogo" },
{ "id": 2, "name": "Dogger" },
{ "id": 3, "name": "Dog" },
{ "id": 4, "name": "Doggernaut" },
{ "id": 5, "name": "Dogging" }
];
const { createServer } = require('the-fake-backend');
const server = createServer(); // if pagination isn't given, default values will be applied
server.routes([
{
path: '/dogs',
methods: [
{
type: 'get', // or MethodType.GET with Typescript
pagination: true,
// pagination: { headers: true } // only this request will have pagination parameters in response headers
},
],
},
]);
server.listen(8080);
Then, given a http://localhost:8080/dogs?page=1&size=2
request, the following payload will be returned:
{
"count": 2,
"empty": false,
"first": true,
"last": false,
"next": true,
"page": 1,
"pages": 3,
"total": 5,
"data": [
{ "id": 3, "name": "Dog" },
{ "id": 4, "name": "Doggernaut" }
]
}
Note: given
http://localhost:8080/dogs?offset=2&size=2
the payload would be the same. Offset attribute has precedence over page.
If headers attribute was set to true in server options (or route pagination options):
const server = createServer({
pagination: {
headers: true,
},
});
Then the metadata attributes would be printed in response headers and the response payload would be the following:
[
{ "id": 3, "name": "Dog" },
{ "id": 4, "name": "Doggernaut" }
]
Just like in Express, route requests may have dynamic params:
const { createServer } = require('the-fake-backend');
const server = createServer();
server.routes([
{
path: '/dogs/:id/details',
methods: [
{
type: 'get', // or MethodType.GET with Typescript
},
],
},
]);
Given a matching HTTP request, e.g. http://localhost:8080/dogs/3/details
, the server will search the following fixtures, sorted by precedence:
data/dogs/3/details.json
data/dogs/:id/details.json
If the request has multiple dynamic params, the precedence is the same, searching the fullly specific fixture, and the fully generic one otherwise.
- Clone this repository
- Run
npm install
to install dependencies - Run
npm start
to start rollup in watch mode - Have fun!
This repository already have an example application that already install last built version from the-fake-backend
before run.
To start this application:
- Go to
example
folder - Run
npm install
to install dependencies - Run
npm start
to start example application