# mockfoundry

> The Mocking-First RESTful Server.

Latest version **1.0.0-alpha.2** (published 2019-03-24) · MIT license · 0 weekly downloads

## Install

```sh
npm install mockfoundry
pnpm add mockfoundry
yarn add mockfoundry
bun add mockfoundry
```

## Health

**Score 15/100 (F)** — status: abandoned.

Positive: no vulnerabilities.

Warnings: low downloads; no types; no esm support.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 1.0.0-alpha.2 |
| Published | 2019-03-24 |
| First published | 2019-03-24 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | none |
| Module format | CommonJS |
| Dependencies | 7 |
| Unpacked size | 44.4 KB |
| Known vulnerabilities | 0 (+10 in 2 direct dependencies) |
| Install scripts | no |
| Author | Kofi Nedjoh |
| Maintainers | koficodedat |
| Keywords | mocking, prototyping, testing, RESTful, CRUD, API, mockfoundry |

## Links

- npm: https://www.npmjs.com/package/mockfoundry
- Repository: https://gitlab.com/koficodedat/mockfoundry
- Issues: https://gitlab.com/koficodedat/mockfoundry/issues
- npm.io page: https://npm.io/package/mockfoundry

## Dependencies (7)

- [fastify](https://npm.io/package/fastify.md) 1.13.3
- [lodash.get](https://npm.io/package/lodash.get.md) ^4.4.2
- [lodash.noop](https://npm.io/package/lodash.noop.md) 3.0.1
- [lodash.merge](https://npm.io/package/lodash.merge.md) 4.6.1
- [lodash.union](https://npm.io/package/lodash.union.md) 4.6.0
- [nedb-promises](https://npm.io/package/nedb-promises.md) 3.0.2
- [lodash.isempty](https://npm.io/package/lodash.isempty.md) ^4.4.0

## Alternatives

- [duck](https://npm.io/package/duck.md) — 4.2M weekly downloads
- [ava](https://npm.io/package/ava.md) — 560.2K weekly downloads
- [storybook-addon-module-mock](https://npm.io/package/storybook-addon-module-mock.md) — 71.7K weekly downloads
- [@ethereum-waffle/mock-contract](https://npm.io/package/@ethereum-waffle/mock-contract.md) — 40.0K weekly downloads
- [aws-elasticsearch-connector](https://npm.io/package/aws-elasticsearch-connector.md) — 37.4K weekly downloads

## Recent versions

- 1.0.0-alpha.2 (latest) — 2019-03-24

## README

[![MOCKFOUNDRY](https://img.shields.io/badge/MOCKFOUNDRY-THE%20MOCKING--FIRST%20RESTFUL%20SERVER-blue.svg?style=for-the-badge)](https://gitlab.com/koficodedat/mockfoundry/wikis/home)


[![Gitlab pipeline status](https://img.shields.io/gitlab/pipeline/koficodedat/mockfoundry.svg?style=for-the-badge)]() 
[![Coverage](https://img.shields.io/codecov/c/gitlab/koficodedat/mockfoundry.svg?style=for-the-badge)](https://codecov.io/gl/koficodedat/mockfoundry/branch/master) 
[![License](https://img.shields.io/npm/l/mockfoundry.svg?style=for-the-badge)]() 
[![Bundle Size](https://img.shields.io/bundlephobia/min/mockfoundry.svg?style=for-the-badge)]() 
[![NPM Version](https://img.shields.io/npm/v/mockfoundry.svg?style=for-the-badge)](https://www.npmjs.com/package/mockfoundry) 
[![NPM Collabs](https://img.shields.io/npm/collaborators/mockfoundry.svg?style=for-the-badge)]()

### Motivation
- Prototyping a UI and want a quick way of testing API/CRUD functionalities?
- Working on a feature with external incomplete API dependencies?
- Ever wanted a very simple RESTful server with built-in CRUD operations?

If you answered **yes** to any of the questions stated above then **mockfoundry** may be right for you.

### Goal
- To *drastically* reduce the boilerplate needed for prototyping with ***actual*** data.
- To enable *true* mocking-out of APIs and still achieving ***close*** to actual world behavior.

### Installation
```
npm i --save mockfoundry // as a dependency
npm i --save-dev mockfoundry // as a development dependency. can be used for testing
```

### Use Require
This library is meant to be run in **Node.js** and therefore is exported as a **CommonJS** library
```
const Mockfoundry = require('mockfoundry'); // require module
```

### Port
Port is an essential part of **mockfoundry** as it is needed to create instances for the server. It is also used in the naming convention for the files in which data is saved. 

If you start one instance on port 1000 and you start another instance on port 1001, I hope you would agree data saved on disk from those endpoints should be saved in their respective files. For that reason, **mockfoundry** treats ports as part of the data file name and maintains them accordingly,thus, **mockfoundry** *fundamentally* treats ports as databases.

The data will persist on disk even between restarts for the same port unless the ***override*** flag is set during instantiation so please tag ports accordingly.

### Instance
```
const instance = new Mockfoundry(port [, override ]);
```

### Run
```
const instance = new Mockfoundry(1000); // create instance on port 1000
instance.start(); // start server
```

### API Primer
All APIs have a similar syntax convention:

	host:port/action/collection?[optional-query-parameters]

Type         |            Meaning           |    Required    |    Default(s)    |
------------ | ---------------------------- | -------------- | ---------------- |
|  **host**  |        host of server        |       Yes      | **localhost** (and is only localhost for now)  |
|  **port**  | port instance is running on  |       Yes      |  No default |
| **action** | crud operation to be performed by the server  | Yes | **save, fetch, count, update, remove** (and these are the only ones that can be used) |
| **collection** | the type of data being saved. It is synonymous to a table in SQL or a type of document in a NoSQL | Yes | No Default |
| **optional-query-parameters** | additional query parameters that can be used along with the **fetch** action to optimize the data returned | No | **skip, limit, sort** (and these are the only ones that can be used) |

### HTTP Method Standards
**Mockfoundry** adheres to the HTTP standards with all API calls. Simply put, all actions use **appropriate** HTTP methods for respective calls.

|   Action     | HTTP Method    | Accepts Body |  Requires Body |        Required In Body        |   Optional In Body  |
| ------------ | -------------- | ------------ | -------------- | -------------------------------| ------------------- |
| **save**     |  **POST**      |     Yes      |      Yes       | array of objects or an object  |          ---        |
| **fetch**    |  **GET**       |     No       |      ---       |              ---               |          ---        |
| **fetch**    |  **POST**      |     Yes      |      No        |              ---               |  fields and filters |
| **count**    |  **GET**       |     No       |      ---       |              ---               |          ---        |
| **count**    |  **POST**      |     Yes      |      No        |              ---               |        filters      |
| **update**   |  **PUT**       |     Yes      |      Yes       |       filters and update       |          ---        |
| **remove**   |  **DELETE**    |     Yes      |      Yes       |             filters            |          ---        |

## APIs
Let's take a Vehicle Collection. We will proceed to learn how to do all **CRUD** operations in **mockfoundry** using our Vehicle Collection.
Our Vehicle Collection has the following structure:
```
{
	brand
	model
	year
	kind
	countryOfOrigin
	spec {
		engine
		drivetrain
		acceleration
		speed
		range
	}
	class
	isElectric
	isStreetLegal
	features
	safetyRating
	awards
	tags
}
```

<mark>The way in which we wrote out this collection will become particularly important [later on](#a-little-about-fields).</mark> Other than that, it is a simple *list* of fields a Vehicle Collection *can* have.

All our examples will be explained using **[axios](https://www.npmjs.com/package/axios)** --- a promise based HTTP client for the browser and node.js.

### Save API
At this point, **mockfoundry** does not enforce any kind of schema validation on a collection level. Anything you save in the collection will be what gets saved. **Mockfoundry** is first and foremost a ***mocking*** library and as such, we want to reduce the boilerplate setup needed to achieve just that.

For the sake of brevity and code readability, we will shorten what we show as being saved but in actuality, we are saving the whole structure described above.

#### Example --- Single Entry
```
axios.post(
	'locahost:1000/save/vehicle',{
		brand: 'bmw',
		model: 'm4',
		year: '2020',
		...... // indicates more fields
	}
)
.then(response => console.log(response)) // prints true if saved, else false
.catch(error => console.error(error)); // prints kind of error
```

#### Example --- Multiple Entry *of the same collection*
```
axios.post(
	'locahost:1000/save/vehicle',[
		{
			brand: 'bmw',
			model: 'm4',
			year: '2020',
			...... // indicates more fields
		},
		{
			brand: 'toyota',
			model: 'prius,
			year: '2021',
			...... // indicates more fields
		}
	]
)
.then(response => console.log(response)) // prints true if saved, else false
.catch(error => console.error(error)); // prints kind of error
```

### Fetch API
Looking back at the **[API Primer](#api-primer)** and **[HTTP Method Standards](#http-method-standards)**, you will notice that the **fetch** action is a very special action. To reiterate, let's list all the combinations of things it can have:
- It can be called with or without a body
- It is the only action that accepts optional query parameters
- A [new fun fact](#a-little-about-fields) that was not apparent from before is, it can accept any arbitrary number of fields in **exactly** the structure described at  the beginning of the **[APIs](#apis)** section. This way of requesting fields is inspired by the **get-what-you-want** model in graphql.

#### Example --- Simple Fetch
```
axios.get('locahost:1000/fetch/vehicle')
.then(data => console.log(data)) // prints an array of vehicle collection objects
.catch(error => console.error(error)); // prints kind of error
```

#### Example --- Fetch with Limit as Query Parameter
```
axios.get('locahost:1000/fetch/vehicle?limit=2')
.then(data => console.log(data)) // prints an array of two vehicle collection objects
.catch(error => console.error(error)); // prints kind of error
```

#### Example --- Fetch with Skip and Limit as Query Parameters
```
axios.get('locahost:1000/fetch/vehicle?skip=1&limit=2')
.then(data => {
	// prints an array of two vehicle collection objects
	// by skipping the first object it finds in the collection
	console.log(data)
})
.catch(error => console.error(error)); // prints kind of error
```

#### Example --- Fetch with Skip, Limit, and Sort as Query Parameters
```
axios.get('locahost:1000/fetch/vehicle?skip=1&limit=2&sort=year')
.then(data => {
	// prints an array of two vehicle collection objects
	// by skipping the first object it finds in the collection
	// and sorting by year ascending order by default
	console.log(data)
})
.catch(error => console.error(error)); // prints kind of error
```
To specify a particular **sort order**, other than the default ascending,  you need to set it with a **colon** character:
```
sort=year:desc
sort=spec.acceleration:asc
```

For multiple sorts, use the **pipe** character to list them out :
```
sort=year:desc|spec.acceleration:asc
sort=year|isElectric:desc
```

#### Example --- Fetch with Filter(s)
```
axios.post(
	'locahost:1000/fetch/vehicle',{
		"filters": [
			{
				"field": "kind",
				"op": "$eq", // special operator character. $eq means equals to.
				"value": "sedan"
			}
		]
	}
)
.then(data => {
	// prints an array of vehicle collection objects
	// that matches the filter(s) passed in
	console.log(data)
})
.catch(error => console.error(error)); // prints kind of error
```

You can head over to the [Filters](#filters) section to learn how to use them. It is very straight forward.

#### Example --- Fetch with Field(s)
```
axios.post(
	'locahost:1000/fetch/vehicle',{
		"fields": `{
				brand
				model
				year
				kind
				spec {
					range
					speed
					engine
				}
				class
				tags
		}
		`
	}
)
.then(data => {
	// prints an array of vehicle collection objects,
	// wih only the fields listed in the fetch call
	console.log(data)
})
.catch(error => console.error(error)); // prints kind of error
```

##### A little about Fields
As [mentioned](#fetch-api) earlier, the **fetch** action has special powers among which is the ability to *list* the fields you want. Thanks to [ES6 string literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals), we can *list* fields we want to return in a multiline format. It has many benefits such as, code readability, familiarity due to its object-like look and getting data in exactly the way fields are listed.
Of course you can resort to using a single line string if the ES6 approach is not viable. The only issue with that is, you loose code readability and I know that will drive me nuts.

To maintain complaince with this structure, **mockfoundry** applies the following validation rules to a fields property:
- Needs to start with an opening curly brace **({)**.
- Needs to end with a closing curly brace **(})**.
- May contain spaces, tabs or new lines.
- Contents between opening and closing curly braces **can only** be any of the rules stated above along with alphanumerics, commas, and underscores.


### Count API
The **count** action is similar to the **fetch** action but different in one obvious way --- it does not need fields or query parameters. 

#### Example --- Simple Count
```
axios.get('locahost:1000/count/vehicle')
.then(data => console.log(data)) // prints the number of documents if any, zero if none
.catch(error => console.error(error)); // prints kind of error
```

#### Example --- Count with Filter(s)
```
axios.post(
	'locahost:1000/count/vehicle',{
		"filters": [
			{
				"field": "kind",
				"op": "$eq", // special operator character. $eq means equals to.
				"value": "sedan"
			}
		]
	}
)
.then(data => {
	// prints the number of documents
	// that matches the filter(s) passed in if anym zero f none
	console.log(data)
})
.catch(error => console.error(error)); // prints kind of error
```

### Update API
The **update** action is similar to the **count** action. However, it **requires** another property called *update*. I know, very original. 

Since **mockfoundry** does not enforce any schema validation due to [reasons](#save-api) stated earlier, the update action ***merges*** new fields with the rest of the collection. 

> **NOTE**: 
>> Updates of inner objects **must** be done with a **dot notation *(parent_prop.child_prop)*** if you want to keep other values that might be in there. Otherwise, if you have all the required data on the client side, you can use the spread operator or any other merge solution to add new data in before updating.

Use the **fetch** action to select the fields you want to return.

#### Example --- Update
```
axios.put(
	'locahost:1000/update/vehicle',{
		"filters": [
			{
				"field": "kind",
				"op": "$eq", // special operator character. $eq means equals to.
				"value": "sedan"
			}
		],
		"update": {
			"year": 2021,
			"spec.engine": "v6" // note dot notation
			"spec.newProp: "some new value"  // note dot notation
			"anotherNewProp: {}
		}
	}
)
.then(response => console.log(response)) // prints true if updated, else false
.catch(error => console.error(error)); // prints kind of error
```

### Remove API
The **remove** action is similar to the **count** action but different one obvious way --- it **requires** the filters property. 

#### Example --- Remove with *required* Filter(s)
```
axios.delete(
	'locahost:1000/remove/vehicle',{
		"filters": [
			{
				"field": "kind",
				"op": "$eq", // special operator character. $eq means equals to.
				"value": "sedan"
			}
		]
	}
)
.then(response => console.log(response)) // prints true if removed, else false
.catch(error => console.error(error)); // prints kind of error
```

Note that, **mockfoundry** treats empty filters array as *match all*. In that sense, an empty filters array is still an valid array and will cause all data of the passed collection to be removed. If that is your intention then ignore this caution.

### Filters
All filter objects follow the **field-op-value** paradigm. That is because **mockfoundry** uses [NeDB](https://www.npmjs.com/package/nedb) under the hood for database operations. 

Besides the **op** property in a filter contstruction, the **field** and the **value** properties are self explanatory. The **op** property is either a logical or comparison operator. Below are the list of all supported operators:

| Operator | Meaning                   | What question does it asks of the collection?                                                                            | Value Type(s)                                 |
| -------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------- |
| $eq      | equal                     | for the given **field**, is any **value** equals what was passed?                                                        | string, number, regex, boolean, object, array |
| $lt      | less than                 | for the given **field**, is any **value** less than what was passed?                                                     | string, number                                |
| $gt      | greater than              | for the given **field**, is any **value** greater than what was passed?                                                  | string, number                                |
| $lte     | less than or equal        | for the given **field**, is any **value** less than or equal to what was passed?                                         | string, number                                |
| $gte     | greater than or equal  to | for the given **field**, is any **value** greater than or equal to what was passed?                                      | string, number                                |
| $neq     | not equal                 | for the given **field**, is any **value** not equal to what was passed?                                                  | string, number, regex, boolean, object, array |
| $exists  | exists                    | for the given **field**, does the **field** *exist* (when **value** is true) or *not exist* (when **value** is false)?   | boolean                                       |
| $regex   | regex                     | for the given **field**, does any **value** match the regex expression passed?                                           | string                                        |
| $not     | not                       | for the given **field**, is the **value** passed not an *exact* match of anything in the collection?                     | string, number, boolean, object, array        |
| $in      | in                        | for the given **field**, is the **value** passed a member of the *array of values* in the collection?                    | array of primitives or objects                |
| $nin     | not in                    | for the given **field**, is the **value** passed not a member of the *array of values* in the collection?                | array of primitives or objects                |
| $or      | or                        | for the given **field**, is *any* of the array of **value**s passed exactly a value in the collection?                   | string, number, boolean, object, array        |
| $and     | and                       | for the given **field**, are *all* of the array of **value**s passed exactly a value in the collection?                  | string, number, boolean, object, array        |

Besides the **$eq** and **$neq** operators, the rest are direct replicas of what [NeDB](https://www.npmjs.com/package/nedb) uses officially. 

### Error
Error is an unexpected but important part of any application. **Mockfoundry** uses [fastify](https://www.fastify.io/) as its application server and draws on its error handling structure and even extends it where possible.

All errors in **mockfoundry** contain the following properties:

| Property   | Meaning                                                                       | Always Present |
| ---------- | ----------------------------------------------------------------------------- | -------------- |
| statusCode | HTTP Code                                                                     | Yes            |
| error      | Type of error                                                                 | Yes            |
| message    | Reason for the error                                                          | Yes            |
| optionals  | A *spread* of any aditional properties specific to the action being performed | No             |

Besides the **optionals** property, all other properties are default and directly borrowed from [fastify](https://www.fastify.io/). The **optionals** property is an object that *merges* its contents with the defaults so there is no actual **optionals** property key in errors.

### Issues
Please [report](https://gitlab.com/koficodedat/mockfoundry/issues) issues as you see them during usage. It will help improve this library as a whole. Thank you.

### Credits
- [Fastify](https://www.npmjs.com/package/fastify)
- [Lodash](https://www.npmjs.com/package/lodash)
- [NeDB](https://www.npmjs.com/package/nedb) / [NeDB Promise](https://www.npmjs.com/package/nedb-promise)

---
_Source: https://npm.io/package/mockfoundry · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
