{"id":15097865,"url":"https://github.com/dwightjack/mokd","last_synced_at":"2025-10-08T02:31:53.511Z","repository":{"id":57205742,"uuid":"75306364","full_name":"dwightjack/mokd","owner":"dwightjack","description":"Programmable fake APIs server","archived":true,"fork":false,"pushed_at":"2018-08-09T07:58:11.000Z","size":71,"stargazers_count":1,"open_issues_count":5,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-09-30T03:56:38.152Z","etag":null,"topics":["api","connect","express","koa","middleware","mock"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dwightjack.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-12-01T15:34:30.000Z","updated_at":"2023-01-28T05:13:59.000Z","dependencies_parsed_at":"2022-09-18T01:22:29.904Z","dependency_job_id":null,"html_url":"https://github.com/dwightjack/mokd","commit_stats":null,"previous_names":["dwightjack/connect-mock-api"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/dwightjack/mokd","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dwightjack%2Fmokd","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dwightjack%2Fmokd/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dwightjack%2Fmokd/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dwightjack%2Fmokd/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dwightjack","download_url":"https://codeload.github.com/dwightjack/mokd/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dwightjack%2Fmokd/sbom","scorecard":{"id":361352,"data":{"date":"2025-08-11","repo":{"name":"github.com/dwightjack/mokd","commit":"de82ebb0a3399dacc1ed60e7c9f2e88600a3f87e"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"project is archived","details":["Warn: Repository is archived."],"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-18T10:55:03.733Z","repository_id":57205742,"created_at":"2025-08-18T10:55:03.733Z","updated_at":"2025-08-18T10:55:03.733Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278646801,"owners_count":26021514,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-06T02:00:05.630Z","response_time":65,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","connect","express","koa","middleware","mock"],"created_at":"2024-09-25T16:40:58.122Z","updated_at":"2025-10-08T02:31:53.249Z","avatar_url":"https://github.com/dwightjack.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# mokd\n\n\u003e Programmable fake APIs server.\n\n\u003c!-- TOC --\u003e\n\n- [Requirements](#requirements)\n- [Installation](#installation)\n- [Usage](#usage)\n    - [Usage as an express/connect middleware](#usage-as-an-expressconnect-middleware)\n    - [Usage as a koa middleware](#usage-as-a-koa-middleware)\n    - [Create your own adapter](#create-your-own-adapter)\n- [Endpoint Configuration](#endpoint-configuration)\n    - [Endpoint properties](#endpoint-properties)\n        - [The `params` object](#the-params-object)\n    - [Path Matching Formats and `$routeMatch`](#path-matching-formats-and-routematch)\n    - [Endpoint response template](#endpoint-response-template)\n    - [Endpoint Base URL](#endpoint-base-url)\n    - [Examples](#examples)\n- [Params interceptors](#params-interceptors)\n- [Response transformers](#response-transformers)\n        - [`transformJSON`](#transformjson)\n        - [`transformText`](#transformtext)\n- [Development Mode](#development-mode)\n    - [Options](#options)\n    - [Methods](#methods)\n- [Contributing](#contributing)\n- [Credits](#credits)\n- [License](#license)\n\n\u003c!-- /TOC --\u003e\n\n## Requirements\n\n* Node.js \u003e=8.9.0 (we strongly suggest to use something like [nvm](https://github.com/creationix/nvm))\n* npm or [yarn](https://yarnpkg.com/lang/en/)\n\n## Installation\n\n```\nnpm install mokd --save\n```\n\n## Usage\n\n```js\nconst { Server }  = require('mokd');\n\nconst server = new Server({\n  baseUrl: '', //optional\n  endpoints: [\n    {\n      path: '/user/',\n      response: {\n        id: 1\n        name: 'John Doe'\n      }\n      //... more endpoints configuration here, see below\n    }\n  ]\n});\n\nconst { data } = server.resolve({ url: '/user/' });\n//data === { \"id\": 1, \"name\": \"John Doe\" }\n```\n\n### Usage as an express/connect middleware\n\nIn order to use the server with an existing connect/express application you need to use the built-in adapter.\n\n```js\nconst express = require('express');\nconst { Server, connectServer }  = require('mokd');\n\nconst server = new Server({\n  baseUrl: '', //optional\n  endpoints: [\n    //... endpoints configuration object here, see below\n  ]\n});\n\nconst app = express();\n\napp.use(connectServer(server));\napp.listen(8000);\n```\n\n### Usage as a koa middleware\n\nIn order to use the server with an existing koa application you need to use the built-in adapter.\n\n```js\nconst Koa = require('koa');\nconst { Server, koaServer }  = require('mokd');\n\nconst server = new Server({\n  baseUrl: '', //optional\n  endpoints: [\n    //... endpoints configuration object here, see below\n  ]\n});\n\nconst app = new Koa();\n\napp.use(koaServer(server));\napp.listen(8000);\n```\n\n### Create your own adapter\n\nIf you're using another application framework, you can create a custom adapter. Refer to the built-in [koa](./lib/koa.js) or [connect](./lib/connect.js) for example implementations.\n\n## Endpoint Configuration\n\nThe core of the server is the `endpoints` configuration option.\n\nYou 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.\n\n```js\nconst { Server }  = require('mokd');\n\n//endpoints as array\nconst server = new Server({\n  endpoints: [\n    //... endpoints configuration object here, see below\n  ]\n});\n\nconst otherServer = new Server({\n  endpoints: (srv) =\u003e [\n    // you can access instance configuration here via `srv.options`\n    //... endpoints configuration object here, see below\n  ]\n});\n```\n\n### Endpoint properties\n\nEndpoints are objects with the following properties:\n\n* `method` (`string`): The expected methods of the incoming request (default: `GET`),\n* `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`),\n* `delay` (`number|function`): force a delay in milliseconds for the response. If a function it must return a number (default: `0`),\n* `contentType` (`string`): Response content type (default: `application/json`),\n* `response` (`*|function`): Response body template. Could be any type of content in relation to the `ContentType` parameter. If a function it will be executed with a `params` object and the `endpoint` itself as arguments. (default: `null`)\n\n#### The `params` object\n\nThe `params` object contains 3 properties:\n\n* `$req`: the original request object\n* `$parsedUrl`: The request URL parsed by NodeJS native `url.parse` method\n* `$routeMatch`: either an object (when `path` is a string) or an array of matched segments (when `path` is a regular expression). See below for details.\n\n\n### Path Matching Formats and `$routeMatch`\n\nEndpoint's `path` configuration could be a plain string, a regular expression or a string with Express-like parameters (see [path-to-regexp](https://www.npmjs.com/package/path-to-regexp) for details).\n`$routeMatch` format will vary based on the provided `path`:\n\n* regular expression: `$routeMatch` will be the resulting array of calling `RegExp.prototype.exec` on it\n* string or Express-like route: `$routeMatch` will be and object with named parameters as keys. Note that even numeric parameters will be strings.\n\nExamples:\n\n```js\n/* Plain string */\n{\n  path: '/api/v1/users'\n  // /api/v1/users/ -\u003e $routeMatch === {}\n}\n\n/* Express-like path */\n{\n  path: '/api/v1/users/:id'\n  // /api/v1/users/10 -\u003e $routeMatch === {id: '10'}\n}\n\n/* RegExp */\n{\n  path: /^\\/api\\/v1\\/users\\/(\\d+)$/\n  // /api/v1/users/10 -\u003e $routeMatch === ['/api/v1/users/10', '10']\n}\n```\n\n### Endpoint response template\n\nAny 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`](#the-params-object) object and the `endpoint` itself as arguments.\n\n### Endpoint Base URL\n\nThe `baseUrl` configuration option sets up a base URL for every relative endpoint path provided. To override the base URL use absolute URLs.\n\n*Note: `baseUrl` applies just to string paths.*\n\n```js\nconst server = new Server({\n  baseUrl: '/api/v1/', //optional\n  endpoints: [\n    {\n      // this endpoint will respond at /api/v1/users\n      path: 'users',\n      response: {\n        // ...\n      }\n    }, {\n      // this endpoint will respond at /custom/path\n      path: '/custom/path',\n      response: {\n        // ...\n      }\n    }\n  ]\n});\n```\n\n### Examples\n\n1) A basic GET endpoint returning a JSON object\n\n```js\nconst endpoint = {\n  path: '/api/v1/user',\n  response: {\n    name: 'John',\n    surname: 'Doe'\n  }\n};\n```\n\n\n2) A GET endpoint returning dynamic data provided by [Chance](http://chancejs.com/)\n\n```js\n\nconst chance = require('chance').Chance();\n\nconst endpoint = {\n  path: '/api/v1/user',\n  response: {\n    name: () =\u003e chance.first(),\n    surname: () =\u003e chance.last()\n  }\n};\n```\n\n3) A GET endpoint matching a regexp and returning a dynamic property based on the match\n\n```js\n\nconst chance = require('chance').Chance();\n\nconst endpoint = {\n  //matches either a male of female user request\n  path: /\\/api\\/v1\\/user\\/(male|female)$/,\n  response: {\n      name: (params) =\u003e chance.first({\n        gender: params.$routeMatch[1]\n      }),\n      surname: (params) =\u003e chance.last({\n        gender: params.$routeMatch[1]\n      })\n  }\n};\n```\n\n4) A GET endpoint matching a regexp and returning a dynamic response based on the match\n\n```js\n\nconst chance = require('chance').Chance();\n\nconst endpoint = {\n  path: '/api/v1/:frag',\n  response: (params) =\u003e {\n    //calling /api/v1/user\n    //params.$routeMatch === {'frag': 'user'}\n    if (params.$routeMatch.frag === 'user') {\n      return {\n        name: () =\u003e chance.first(),\n        surname: () =\u003e chance.last()\n      };\n    }\n\n    return {\n      error: 'Not matching anything'\n    };\n  }\n};\n```\n\n5) A POST endpoint, accepting a body request and returning a success message\n\n_Note:_ to parse the request body you usually need to enable body parsing in your application (in express / connect you can use [body-parser](https://github.com/expressjs/body-parser)).\n\n```js\nconst endpoint = {\n  path: '/api/v1/send',\n  method: 'POST',\n  response: (params) =\u003e {\n    if (!params.$req.body.username || !params.$req.body.password) {\n      return {\n          success: false,\n          msg: 'You must provide a username and a password'\n      };\n    }\n\n    return {\n      success: true,\n      msg: 'Succesfully logged in!'\n    };\n  }\n};\n```\n\n## Params interceptors\n\nThe [params](#the-params-object) 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.\n\nEvery interceptor function receives the params object filtered by the previous interceptor.\n\n```js\nconst addCustomKey = (params) =\u003e Object.assign(params, { keyID: 'my-custom-id' });\nconst addIfKey = (params) =\u003e Object.assign(params, params.keyID \u0026\u0026 { someData: '...' });\n\nconst server = new Server({\n  interceptors: [\n    addCustomKey,\n    addIfKey\n  ]\n});\n```\n\n## Response transformers\n\nYou can instruct the server on which data format it has to provide for each endpoint's `contentType` with _response transformers_.\n\nResponse transformers are functions that take in 3 arguments and return formatted data (usually stringified in order the be a valid server response).\n\nThe arguments are:\n\n* `data`: raw data object generated by the `response` property of the endpoint. Note that at this point dynamic response fields have not been yet been processed\n* `endpoint`: matched endpoint for the request\n* `params`: param object as used in the `response` property of the endpoint\n\nAn 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`.\n\nBy default the server comes with two built-in transformers:\n\n#### `transformJSON`\n\nResolves any dynamic response template key and returns a stringified JSON object.\n\nIt's an high order function that takes a JSON stringifier function as first argument (defaults to `JSON.stringify`).\n\n```js\nconst { Server } = require('mokd');\nconst { transformJSON } = require('mokd/lib/utils');\n\ncost server = new Server({\n  endpoints: [ ... ],\n  transformers: [\n    transformJSON()\n  ]\n});\n```\n\n#### `transformText`\n\nReturns a string representation of the data provided by the endpoint's response.\n\n```js\nconst { Server } = require('mokd');\nconst { transformText } = require('mokd/lib/utils');\n\ncost server = new Server({\n  endpoints: [ ... ],\n  transformers: [\n    transformText\n  ]\n});\n```\n\n## Development Mode\n\nMocked 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.\n\nIn 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.\n\n```js\n// endpoints.js\nmodule.exports = [{\n  path: 'api/users/'\n  response {\n    // ...\n  }\n}];\n\n// server.js\nconst { createWatcher, Server } = require('../index');\n\nconst server = new Server(); // \u003c-- don't pass endpoints here\n\nconst watcher = createWatcher({\n  server,\n  entrypoint: './endpoints.js'\n  paths: ['./mocks/**/*.*'] //additional paths to watch\n});\n\nwatcher.start();\n```\n\n### Options\n\n`createWatcher` config object as the following options:\n\n* `server`: A mock server instance\n* `cwd`: (default: `process.cwd()`) The base directory from which relative paths are resolved.\n* `entrypoint`: A path to a file exposing a list of endpoints. Either absolute or relative to `cwd`\n* `paths`: A list of files or patterns to be watched for changes (See [`chokidar.watch` `path` argument](https://github.com/paulmillr/chokidar#api) for details).\n* `watchOptions`: [`chokidar.watch` options](https://github.com/paulmillr/chokidar#api). By default `ignoreInitial` is `true` and `cwd` has the same value as the `cwd` option here.\n\n### Methods\n\n`createWatcher` returned object has the following methods:\n\n* `start`: runs the watcher.\n* `close`: proxy to chockidar's [`close` method](https://github.com/paulmillr/chokidar#methods--events)\n* `on`: proxy to chockidar's [`on` method](https://github.com/paulmillr/chokidar#methods--events)\n* `update(clear = true)`: Loads a fresh copy of the `entrypoint` file and every watched file. To reload just the entrypoint set `clear` to `false`.\n* `clearCache`: removes watched files from NodeJS module's cache.\n\n\n## Contributing\n\n1. Fork it or clone the repo\n1. Install dependencies `npm install`\n1. Run `npm start` to launch a development server\n1. Code your changes and write new tests in the `tests` folder.\n1. Ensure everything is fine by running `npm test` and `npm run eslint`\n1. Push it or submit a pull request :D\n\n## Credits\n\nCreated by Marco Solazzi\n\n## License\n\n[MIT](LICENSE)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdwightjack%2Fmokd","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdwightjack%2Fmokd","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdwightjack%2Fmokd/lists"}