{"id":13653143,"url":"https://github.com/marmelab/FakeRest","last_synced_at":"2025-04-23T06:31:09.352Z","repository":{"id":29281841,"uuid":"32814581","full_name":"marmelab/FakeRest","owner":"marmelab","description":"Patch fetch/XMLHttpRequest to fake a REST API server in the browser, based on JSON data.","archived":false,"fork":false,"pushed_at":"2025-02-06T09:37:41.000Z","size":1413,"stargazers_count":449,"open_issues_count":0,"forks_count":48,"subscribers_count":18,"default_branch":"master","last_synced_at":"2025-04-21T06:08:47.840Z","etag":null,"topics":["client","fetch","frontend","mock","rest","test","xmlhttprequest"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/marmelab.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-03-24T17:38:54.000Z","updated_at":"2025-04-20T09:20:20.000Z","dependencies_parsed_at":"2023-09-26T01:43:32.476Z","dependency_job_id":"6b63bd5f-53e4-439e-9fb8-04506b71b568","html_url":"https://github.com/marmelab/FakeRest","commit_stats":{"total_commits":256,"total_committers":14,"mean_commits":"18.285714285714285","dds":0.51171875,"last_synced_commit":"73f26e113a6631853e155b8485b37418705abda4"},"previous_names":[],"tags_count":32,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marmelab%2FFakeRest","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marmelab%2FFakeRest/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marmelab%2FFakeRest/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marmelab%2FFakeRest/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/marmelab","download_url":"https://codeload.github.com/marmelab/FakeRest/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250384849,"owners_count":21421803,"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","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":["client","fetch","frontend","mock","rest","test","xmlhttprequest"],"created_at":"2024-08-02T02:01:06.391Z","updated_at":"2025-04-23T06:31:06.185Z","avatar_url":"https://github.com/marmelab.png","language":"TypeScript","readme":"# FakeRest\n\nA browser library that intercepts AJAX calls to mock a REST server based on JSON data.\n\nUse it in conjunction with [MSW](https://mswjs.io/), [fetch-mock](https://www.wheresrhys.co.uk/fetch-mock/), or [Sinon.js](https://sinonjs.org/releases/v18/fake-xhr-and-server/) to test JavaScript REST clients on the client side (e.g. single page apps) without a server.\n\nSee it in action in the [react-admin](https://marmelab.com/react-admin/) [demo](https://marmelab.com/react-admin-demo) ([source code](https://github.com/marmelab/react-admin/tree/master/examples/demo)).\n\n## Installation\n\n```sh\nnpm install fakerest --save-dev\n```\n\n## Usage\n\nFakeRest lets you create a handler function that you can pass to an API mocking library. FakeRest supports [MSW](https://mswjs.io/), [fetch-mock](https://www.wheresrhys.co.uk/fetch-mock/), and [Sinon](https://sinonjs.org/releases/v18/fake-xhr-and-server/). If you have the choice, we recommend using MSW, as it will allow you to inspect requests as you usually do in the dev tools network tab.\n\n### MSW\n\nInstall [MSW](https://mswjs.io/) and initialize it:\n\n```sh\nnpm install msw@latest --save-dev\nnpx msw init \u003cPUBLIC_DIR\u003e # eg: public\n```\n\nThen configure an MSW worker:\n\n```js\n// in ./src/fakeServer.js\nimport { http } from 'msw';\nimport { setupWorker } from \"msw/browser\";\nimport { getMswHandler } from \"fakerest\";\n\nconst handler = getMswHandler({\n    baseUrl: 'http://localhost:3000',\n    data: {\n        'authors': [\n            { id: 0, first_name: 'Leo', last_name: 'Tolstoi' },\n            { id: 1, first_name: 'Jane', last_name: 'Austen' }\n        ],\n        'books': [\n            { id: 0, author_id: 0, title: 'Anna Karenina' },\n            { id: 1, author_id: 0, title: 'War and Peace' },\n            { id: 2, author_id: 1, title: 'Pride and Prejudice' },\n            { id: 3, author_id: 1, title: 'Sense and Sensibility' }\n        ],\n        'settings': {\n            language: 'english',\n            preferred_format: 'hardback',\n        }\n    }\n});\nexport const worker = setupWorker(\n    // Make sure you use a RegExp to target all calls to the API\n    http.all(/http:\\/\\/localhost:3000/, handler)\n);\n```\n\nFinally, call the `worker.start()` method before rendering your application. For instance, in a Vite React application:\n\n```js\nimport React from \"react\";\nimport ReactDom from \"react-dom\";\nimport { App } from \"./App\";\nimport { worker } from \"./fakeServer\";\n\nworker.start({\n    quiet: true, // Instruct MSW to not log requests in the console\n    onUnhandledRequest: 'bypass', // Instruct MSW to ignore requests we don't handle\n}).then(() =\u003e {\n  ReactDom.render(\u003cApp /\u003e, document.getElementById(\"root\"));\n});\n```\n\nFakeRest will now intercept every `fetch` request to the REST server.\n\n### fetch-mock\n\nInstall [fetch-mock](https://www.wheresrhys.co.uk/fetch-mock/):\n\n```sh\nnpm install fetch-mock --save-dev\n```\n\nYou can then create a handler and pass it to fetch-mock:\n\n```js\nimport fetchMock from 'fetch-mock';\nimport { getFetchMockHandler } from \"fakerest\";\n\nconst handler = getFetchMockHandler({\n    baseUrl: 'http://localhost:3000',\n    data: {\n        'authors': [\n            { id: 0, first_name: 'Leo', last_name: 'Tolstoi' },\n            { id: 1, first_name: 'Jane', last_name: 'Austen' }\n        ],\n        'books': [\n            { id: 0, author_id: 0, title: 'Anna Karenina' },\n            { id: 1, author_id: 0, title: 'War and Peace' },\n            { id: 2, author_id: 1, title: 'Pride and Prejudice' },\n            { id: 3, author_id: 1, title: 'Sense and Sensibility' }\n        ],\n        'settings': {\n            language: 'english',\n            preferred_format: 'hardback',\n        }\n    }\n});\n\nfetchMock.mock('begin:http://localhost:3000', handler);\n```\n\nFakeRest will now intercept every `fetch` request to the REST server.\n\n### Sinon\n\nInstall [Sinon](https://sinonjs.org/releases/v18/fake-xhr-and-server/):\n\n```sh\nnpm install sinon --save-dev\n```\n\nThen, configure a Sinon server:\n\n```js\nimport sinon from 'sinon';\nimport { getSinonHandler } from \"fakerest\";\n\nconst handler = getSinonHandler({\n    baseUrl: 'http://localhost:3000',\n    data: {\n        'authors': [\n            { id: 0, first_name: 'Leo', last_name: 'Tolstoi' },\n            { id: 1, first_name: 'Jane', last_name: 'Austen' }\n        ],\n        'books': [\n            { id: 0, author_id: 0, title: 'Anna Karenina' },\n            { id: 1, author_id: 0, title: 'War and Peace' },\n            { id: 2, author_id: 1, title: 'Pride and Prejudice' },\n            { id: 3, author_id: 1, title: 'Sense and Sensibility' }\n        ],\n        'settings': {\n            language: 'english',\n            preferred_format: 'hardback',\n        }\n    },\n});\n\n// use sinon.js to monkey-patch XmlHttpRequest\nconst sinonServer = sinon.fakeServer.create();\n// this is required when doing asynchronous XmlHttpRequest\nsinonServer.autoRespond = true;\nsinonServer.respondWith(handler);\n```\n\nFakeRest will now intercept every `XMLHttpRequest` request to the REST server.\n\n## REST Syntax\n\nFakeRest uses a simple REST syntax described below.\n\n### Get A Collection of records\n\n`GET /[name]` returns an array of records in the `name` collection. It accepts 4 query parameters: `filter`, `sort`, `range`, and `embed`. It responds with a status 200 if there is no pagination, or 206 if the list of items is paginated. The response mentions the total count in the `Content-Range` header.\n\n    GET /books?filter={\"author_id\":1}\u0026embed=[\"author\"]\u0026sort=[\"title\",\"desc\"]\u0026range=[0-9]\n\n    HTTP 1.1 200 OK\n    Content-Range: items 0-1/2\n    Content-Type: application/json\n    [\n        { \"id\": 3, \"author_id\": 1, \"title\": \"Sense and Sensibility\", \"author\": { \"id\": 1, \"first_name\": \"Jane\", \"last_name\": \"Austen\" } },\n        { \"id\": 2, \"author_id\": 1, \"title\": \"Pride and Prejudice\", \"author\": { \"id\": 1, \"first_name\": \"Jane\", \"last_name\": \"Austen\" } }\n    ]\n\nThe `filter` param must be a serialized object literal describing the criteria to apply to the search query. See the [supported filters](#supported-filters) for more details.\n\n    GET /books?filter={\"author_id\":1} // return books where author_id is equal to 1\n    HTTP 1.1 200 OK\n    Content-Range: items 0-1/2\n    Content-Type: application/json\n    [\n        { \"id\": 2, \"author_id\": 1, \"title\": \"Pride and Prejudice\" },\n        { \"id\": 3, \"author_id\": 1, \"title\": \"Sense and Sensibility\" }\n    ]\n\n    // array values are possible\n    GET /books?filter={\"id\":[2,3]} // return books where id is in [2,3]\n    HTTP 1.1 200 OK\n    Content-Range: items 0-1/2\n    Content-Type: application/json\n    [\n        { \"id\": 2, \"author_id\": 1, \"title\": \"Pride and Prejudice\" },\n        { \"id\": 3, \"author_id\": 1, \"title\": \"Sense and Sensibility\" }\n    ]\n\n    // use the special \"q\" filter to make a full-text search on all text fields\n    GET /books?filter={\"q\":\"and\"} // return books where any of the book properties contains the string 'and'\n\n    HTTP 1.1 200 OK\n    Content-Range: items 0-2/3\n    Content-Type: application/json\n    [\n        { \"id\": 1, \"author_id\": 0, \"title\": \"War and Peace\" },\n        { \"id\": 2, \"author_id\": 1, \"title\": \"Pride and Prejudice\" },\n        { \"id\": 3, \"author_id\": 1, \"title\": \"Sense and Sensibility\" }\n    ]\n\n    // use _gt, _gte, _lte, _lt, or _neq suffix on filter names to make range queries\n    GET /books?filter={\"price_lte\":20} // return books where the price is less than or equal to 20\n    GET /books?filter={\"price_gt\":20} // return books where the price is greater than 20\n\n    // when the filter object contains more than one property, the criteria combine with an AND logic\n    GET /books?filter={\"published_at_gte\":\"2015-06-12\",\"published_at_lte\":\"2015-06-15\"} // return books published between two dates\n\nThe `sort` param must be a serialized array literal defining first the property used for sorting, then the sorting direction.\n\n    GET /author?sort=[\"date_of_birth\",\"asc\"]  // return authors, the oldest first\n    GET /author?sort=[\"date_of_birth\",\"desc\"]  // return authors, the youngest first\n\nThe `range` param defines the number of results by specifying the rank of the first and last results. The first result is #0.\n\n    GET /books?range=[0-9] // return the first 10 books\n    GET /books?range=[10-19] // return the 10 next books\n\nThe `embed` param sets the related objects or collections to be embedded in the response.\n\n    // embed author in books\n    GET /books?embed=[\"author\"]\n    HTTP 1.1 200 OK\n    Content-Range: items 0-3/4\n    Content-Type: application/json\n    [\n        { \"id\": 0, \"author_id\": 0, \"title\": \"Anna Karenina\", \"author\": { \"id\": 0, \"first_name\": \"Leo\", \"last_name\": \"Tolstoi\" } },\n        { \"id\": 1, \"author_id\": 0, \"title\": \"War and Peace\", \"author\": { \"id\": 0, \"first_name\": \"Leo\", \"last_name\": \"Tolstoi\" } },\n        { \"id\": 2, \"author_id\": 1, \"title\": \"Pride and Prejudice\", \"author\": { \"id\": 1, \"first_name\": \"Jane\", \"last_name\": \"Austen\" } },\n        { \"id\": 3, \"author_id\": 1, \"title\": \"Sense and Sensibility\", \"author\": { \"id\": 1, \"first_name\": \"Jane\", \"last_name\": \"Austen\" } }\n    ]\n\n    // embed books in author\n    GET /authors?embed=[\"books\"]\n    HTTP 1.1 200 OK\n    Content-Range: items 0-1/2\n    Content-Type: application/json\n    [\n        { id: 0, first_name: 'Leo', last_name: 'Tolstoi', books: [{ id: 0, author_id: 0, title: 'Anna Karenina' }, { id: 1, author_id: 0, title: 'War and Peace' }] },\n        { id: 1, first_name: 'Jane', last_name: 'Austen', books: [{ id: 2, author_id: 1, title: 'Pride and Prejudice' }, { id: 3, author_id: 1, title: 'Sense and Sensibility' }] }\n    ]\n\n    // you can embed several objects\n    GET /authors?embed=[\"books\",\"country\"]\n\n### Get A Single Record\n\n`GET /[name]/:id` returns a JSON object, and a status 200, unless the resource doesn't exist.\n\n    GET /books/2\n\n    HTTP 1.1 200 OK\n    Content-Type: application/json\n    { \"id\": 2, \"author_id\": 1, \"title\": \"Pride and Prejudice\" }\n\nThe `embed` param sets the related objects or collections to be embedded in the response.\n\n    GET /books/2?embed=['author']\n\n    HTTP 1.1 200 OK\n    Content-Type: application/json\n    { \"id\": 2, \"author_id\": 1, \"title\": \"Pride and Prejudice\", \"author\": { \"id\": 1, \"first_name\": \"Jane\", \"last_name\": \"Austen\" } }\n\n### Create A Record\n\n`POST /[name]` returns a status 201 with a `Location` header for the newly created resource, and the new resource in the body.\n\n    POST /books\n    { \"author_id\": 1, \"title\": \"Emma\" }\n\n    HTTP 1.1 201 Created\n    Location: /books/4\n    Content-Type: application/json\n    { \"author_id\": 1, \"title\": \"Emma\", \"id\": 4 }\n\n### Update A Record\n\n`PUT /[name]/:id` returns the modified JSON object, and a status 200, unless the resource doesn't exist.\n\n    PUT /books/2\n    { \"author_id\": 1, \"title\": \"Pride and Prejudice\" }\n\n    HTTP 1.1 200 OK\n    Content-Type: application/json\n    { \"id\": 2, \"author_id\": 1, \"title\": \"Pride and Prejudice\" }\n\n### Delete A Single Record\n\n`DELETE /[name]/:id` returns the deleted JSON object, and a status 200, unless the resource doesn't exist.\n\n    DELETE /books/2\n\n    HTTP 1.1 200 OK\n    Content-Type: application/json\n    { \"id\": 2, \"author_id\": 1, \"title\": \"Pride and Prejudice\" }\n\n### Supported Filters\n\nOperators are specified as suffixes on each filtered field. For instance, applying the `_lte` operator on the `price` field for the `books` resource is done like this:\n\n        GET /books?filter={\"price_lte\":20} // return books where the price is less than or equal to 20\n\n- `_eq`: check for equality on simple values:\n\n        GET /books?filter={\"price_eq\":20} // return books where the price is equal to 20\n\n- `_neq`: check for inequality on simple values\n\n        GET /books?filter={\"price_neq\":20} // return books where the price is not equal to 20\n\n- `_eq_any`: check for equality on any passed values\n\n        GET /books?filter={\"price_eq_any\":[20, 30]} // return books where the price is equal to 20 or 30\n\n- `_neq_any`: check for inequality on any passed values\n\n        GET /books?filter={\"price_neq_any\":[20, 30]} // return books where the price is not equal to 20 nor 30\n\n- `_inc_any`: check for items that include any of the passed values\n\n        GET /books?filter={\"authors_inc_any\":['William Gibson', 'Pat Cadigan']} // return books where authors include either 'William Gibson' or 'Pat Cadigan' or both\n\n- `_q`: check for items that contain the provided text\n\n        GET /books?filter={\"author_q\":['Gibson']} // return books where the author includes 'Gibson' not considering the other fields\n\n- `_lt`: check for items that have a value lower than the provided value\n\n        GET /books?filter={\"price_lte\":100} // return books that have a price lower that 100\n\n- `_lte`: check for items that have a value lower than or equal to the provided value\n\n        GET /books?filter={\"price_lte\":100} // return books that have a price lower or equal to 100\n\n- `_gt`: check for items that have a value greater than the provided value\n\n        GET /books?filter={\"price_gte\":100} // return books that have a price greater that 100\n\n- `_gte`: check for items that have a value greater than or equal to the provided value\n\n        GET /books?filter={\"price_gte\":100} // return books that have a price greater or equal to 100\n\n### Single Elements\n\nFakeRest allows you to define a single element, such as a user profile or global settings, that can be fetched, updated, or deleted.\n\n    GET /settings\n\n    HTTP 1.1 200 OK\n    Content-Type: application/json\n    { \"language\": \"english\", \"preferred_format\": \"hardback\" }\n\n    PUT /settings\n    { \"language\": \"french\", \"preferred_format\": \"paperback\" }\n\n    HTTP 1.1 200 OK\n    Content-Type: application/json\n    { \"language\": \"french\", \"preferred_format\": \"paperback\" }\n\n    DELETE /settings\n\n    HTTP 1.1 200 OK\n    Content-Type: application/json\n    { \"language\": \"french\", \"preferred_format\": \"paperback\" }\n\n## Middlewares\n\nMiddlewares let you intercept requests and simulate server features such as:\n - authentication checks\n - server-side validation\n - server dynamically generated values\n - simulate response delays\n\nYou can define middlewares on all handlers, by passing a `middlewares` option:\n\n```js\nimport { getMswHandler } from 'fakerest';\nimport { data } from './data';\n\nconst handler = getMswHandler({\n    baseUrl: 'http://my.custom.domain',\n    data,\n    middlewares: [\n        async (context, next) =\u003e {\n            if (context.headers.Authorization === undefined) {\n                return {\n                    status: 401,\n                    headers: {},\n                };\n            }\n\n            return next(context);\n        },\n        withDelay(300),\n    ],\n});\n```\n\nA middleware is a function that receives 2 parameters:\n - The FakeRest `context`, an object containing the data extracted from the request that FakeRest uses to build the response. It has the following properties:\n - `method`: The request method as a string (`GET`, `POST`, `PATCH` or `PUT`)\n - `url`: The request URL as a string\n - `headers`: The request headers as an object where keys are header names\n - `requestBody`: The parsed request data if any\n - `params`: The request parameters from the URL search (e.g. the identifier of the requested record)\n - `collection`: The name of the targeted [collection](#collection) (e.g. `posts`)\n - `single`: The name of the targeted [single](#single) (e.g. `settings`)\n - A `next` function to call the next middleware in the chain, to which you must pass the `context`\n\nA middleware must return a FakeRest response either by returning the result of the `next` function or by returning its own response. A FakeRest response is an object with the following properties:\n - `status`: The response status as a number (e.g. `200`)\n - `headers`: The response HTTP headers as an object where keys are header names\n - `body`: The response body which will be stringified\n\n### Authentication Checks\n\nHere's how to implement an authentication check:\n\n```js\nconst handler = getMswHandler({\n    baseUrl: 'http://my.custom.domain',\n    data,\n    middlewares: [\n        async (context, next) =\u003e {\n            if (context.headers.Authorization === undefined) {\n                return { status: 401, headers: {} };\n            }\n            return next(context);\n        }\n    ]\n});\n```\n\n### Server-Side Validation\n\nHere's how to implement server-side validation:\n\n```js\nconst handler = getMswHandler({\n    baseUrl: 'http://my.custom.domain',\n    data,\n    middlewares: [\n        async (context, next) =\u003e {\n            if (\n                context.collection === \"books\" \u0026\u0026\n                request.method === \"POST\" \u0026\u0026\n !context.requestBody?.title\n            ) {\n                return {\n                    status: 400,\n                    headers: {},\n                    body: {\n                        errors: {\n                            title: 'An article with this title already exists. The title must be unique.',\n                        },\n                    },\n                };\n            }\n\n            return next(context);\n        }\n    ]\n});\n```\n\n### Dynamically Generated Values\n\nHere's how to implement dynamically generated values on creation:\n\n```js\nconst handler = getMswHandler({\n    baseUrl: 'http://my.custom.domain',\n    data,\n    middlewares: [\n        async (context, next) =\u003e {\n            if (\n                context.collection === 'books' \u0026\u0026\n                context.method === 'POST'\n            ) {\n                const response = await next(context);\n                response.body.updatedAt = new Date().toISOString();\n                return response;\n            }\n\n            return next(context);\n        }\n    ]\n});\n```\n\n### Simulate Response Delays\n\nHere's how to simulate response delays:\n\n```js\nconst handler = getMswHandler({\n    baseUrl: 'http://my.custom.domain',\n    data,\n    middlewares: [\n        async (context, next) =\u003e {\n            return new Promise((resolve) =\u003e {\n                setTimeout(() =\u003e {\n                    resolve(next(context));\n                }, 500);\n            });\n        }\n    ]\n});\n```\n\nThis is so common FakeRest provides the `withDelay` function for that:\n\n```js\nimport { getMswHandler, withDelay } from 'fakerest';\n\nconst handler = getMswHandler({\n    baseUrl: 'http://my.custom.domain',\n    data,\n    middlewares: [\n        withDelay(500), // delay in ms\n    ]\n});\n```\n\n## Configuration\n\nAll handlers can be customized to accommodate your API structure.\n\n### Identifiers\n\nBy default, FakeRest assumes all records have a unique `id` field.\nSome databases such as [MongoDB](https://www.mongodb.com) use `_id` instead of `id` for collection identifiers.\nYou can customize FakeRest to do the same by setting the `identifierName` option:\n\n```js\nconst handler = getMswHandler({\n    baseUrl: 'http://my.custom.domain',\n    data,\n    identifierName: '_id'\n});\n```\n\nYou can also specify that on a per-collection basis:\n\n```js\nimport { MswAdapter, Collection } from 'fakerest';\n\nconst adapter = new MswAdapter({ baseUrl: 'http://my.custom.domain', data });\nconst authorsCollection = new Collection({ items: [], identifierName: '_id' });\nadapter.server.addCollection('authors', authorsCollection);\nconst handler = adapter.getHandler();\n```\n\n### Primary Keys\n\nBy default, FakeRest uses an auto-incremented sequence for the item identifiers.\nIf you'd rather use UUIDs for instance but would like to avoid providing them when you insert new items, you can provide your own function:\n\n```js\nimport { getMswHandler } from 'fakerest';\nimport uuid from 'uuid';\n\nconst handler = new getMswHandler({\n    baseUrl: 'http://my.custom.domain',\n    data,\n    getNewId: () =\u003e uuid.v5()\n});\n```\n\nYou can also specify that on a per-collection basis:\n\n```js\nimport { MswAdapter, Collection } from 'fakerest';\nimport uuid from 'uuid';\n\nconst adapter = new MswAdapter({ baseUrl: 'http://my.custom.domain', data });\nconst authorsCollection = new Collection({ items: [], getNewId: () =\u003e uuid.v5() });\nadapter.server.addCollection('authors', authorsCollection);\nconst handler = adapter.getHandler();\n```\n\n### Default Queries\n\nSome APIs might enforce some parameters on queries. For instance, an API might always include an [embed](#embed) or enforce a query filter.\nYou can simulate this using the `defaultQuery` parameter:\n\n```js\nimport { getMswHandler } from 'fakerest';\nimport uuid from 'uuid';\n\nconst handler = getMswHandler({\n    baseUrl: 'http://my.custom.domain',\n    data,\n    defaultQuery: (collection) =\u003e {\n        if (resourceName == 'authors') return { embed: ['books'] }\n        if (resourceName == 'books') return { filter: { published: true } }\n        return {};\n    }\n});\n```\n\n## Architecture\n\nBehind a simple API (`getXXXHandler`), FakeRest uses a modular architecture that lets you combine different components to build a fake REST server that fits your needs.\n\n### Mocking Adapter\n\n`getXXXHandler` is a shortcut to an object-oriented API of adapter classes:\n\n```js\nexport const getMswHandler = (options: MswAdapterOptions) =\u003e {\n    const server = new MswAdapter(options);\n    return server.getHandler();\n};\n```\n\nFakeRest provides 3 adapter classes:\n\n- `MswAdapter`: Based on [MSW](https://mswjs.io/)\n- `FetchMockAdapter`: Based on [`fetch-mock`](https://www.wheresrhys.co.uk/fetch-mock/)\n- `SinonAdapter`: Based on [Sinon](https://sinonjs.org/releases/v18/fake-xhr-and-server/)\n\nYou can use the adapter class directly, e.g. if you want to make the adapter instance available in the global scope for debugging purposes:\n\n```js\nimport { MsWAdapter } from 'fakerest';\n\nconst adapter = new MswAdapter({\n    baseUrl: 'http://my.custom.domain',\n    data: {\n        'authors': [\n            { id: 0, first_name: 'Leo', last_name: 'Tolstoi' },\n            { id: 1, first_name: 'Jane', last_name: 'Austen' }\n        ],\n        'books': [\n            { id: 0, author_id: 0, title: 'Anna Karenina' },\n            { id: 1, author_id: 0, title: 'War and Peace' },\n            { id: 2, author_id: 1, title: 'Pride and Prejudice' },\n            { id: 3, author_id: 1, title: 'Sense and Sensibility' }\n        ],\n        'settings': {\n            language: 'english',\n            preferred_format: 'hardback',\n        }\n    }\n});\nwindow.fakerest = adapter;\nconst handler = adapter.getHandler();\n```\n\n### REST Server\n\nAdapters transform requests to a normalized format, pass them to a server object, and transform the normalized server response into the format expected by the mocking library.\n\nThe server object implements the REST syntax. It takes a normalized request and exposes a `handle` method that returns a normalized response. FakeRest currently provides only one server implementation: `SimpleRestServer`.\n\nYou can specify the server to use in an adapter by passing the `server` option:\n\n```js\nconst server = new SimpleRestServer({\n    baseUrl: 'http://my.custom.domain',\n    data: {\n        'authors': [\n            { id: 0, first_name: 'Leo', last_name: 'Tolstoi' },\n            { id: 1, first_name: 'Jane', last_name: 'Austen' }\n        ],\n        'books': [\n            { id: 0, author_id: 0, title: 'Anna Karenina' },\n            { id: 1, author_id: 0, title: 'War and Peace' },\n            { id: 2, author_id: 1, title: 'Pride and Prejudice' },\n            { id: 3, author_id: 1, title: 'Sense and Sensibility' }\n        ],\n        'settings': {\n            language: 'english',\n            preferred_format: 'hardback',\n        }\n    }\n});\nconst adapter = new MswAdapter({ server });\nconst handler = adapter.getHandler();\n```\n\nYou can provide an alternative server implementation. This class must implement the `APIServer` type:\n\n```ts\nexport type APIServer = {\n    baseUrl?: string;\n    handle: (context: FakeRestContext) =\u003e Promise\u003cBaseResponse\u003e;\n};\n\nexport type BaseResponse = {\n    status: number;\n    body?: Record\u003cstring, any\u003e | Record\u003cstring, any\u003e[];\n    headers: { [key: string]: string };\n};\n\nexport type FakeRestContext = {\n    url?: string;\n    headers?: Headers;\n    method?: string;\n    collection?: string;\n    single?: string;\n    requestBody: Record\u003cstring, any\u003e | undefined;\n    params: { [key: string]: any };\n};\n```\n\nThe `FakerRestContext` type describes the normalized request. It's usually the adapter's job to transform the request from the mocking library to this format.\n\n### Database\n\nThe querying logic is implemented in a class called `Database`, which is independent of the server. It contains [collections](#collections) and [single](#single).\n\nYou can specify the database used by a server by setting its `database` property:\n\n```js\nconst database = new Database({\n    data: {\n        'authors': [\n            { id: 0, first_name: 'Leo', last_name: 'Tolstoi' },\n            { id: 1, first_name: 'Jane', last_name: 'Austen' }\n        ],\n        'books': [\n            { id: 0, author_id: 0, title: 'Anna Karenina' },\n            { id: 1, author_id: 0, title: 'War and Peace' },\n            { id: 2, author_id: 1, title: 'Pride and Prejudice' },\n            { id: 3, author_id: 1, title: 'Sense and Sensibility' }\n        ],\n        'settings': {\n            language: 'english',\n            preferred_format: 'hardback',\n        }\n    }\n});\nconst server = new SimpleRestServer({ baseUrl: 'http://my.custom.domain', database });\n```\n\nYou can even use the database object if you want to manipulate the data:\n\n```js\ndatabase.updateOne('authors', 0, { first_name: 'Lev' });\n```\n\n### Collections \u0026 Singles\n\nThe Database may contain collections and singles. In the following example, `authors` and `books` are collections, and `settings` is a single.\n\n```js\nconst handler = getMswHandler({\n    baseUrl: 'http://localhost:3000',\n    data: {\n        'authors': [\n            { id: 0, first_name: 'Leo', last_name: 'Tolstoi' },\n            { id: 1, first_name: 'Jane', last_name: 'Austen' }\n            ],\n        'books': [\n            { id: 0, author_id: 0, title: 'Anna Karenina' },\n            { id: 1, author_id: 0, title: 'War and Peace' },\n            { id: 2, author_id: 1, title: 'Pride and Prejudice' },\n            { id: 3, author_id: 1, title: 'Sense and Sensibility' }\n        ],\n        'settings': {\n            language: 'english',\n            preferred_format: 'hardback',\n        }\n    }\n});\n```\n\nA collection is the equivalent of a classic database table. It supports filtering and direct access to records by their identifier.\n\nA single represents an API endpoint that returns a single entity. It's useful for things such as user profile routes (`/me`) or global settings (`/settings`).\n\n### Embeds\n\nFakeRest supports embedding other resources in a main resource query result. For instance, embedding the author of a book.\n\n    GET /books/2?embed=['author']\n\n    HTTP 1.1 200 OK\n    Content-Type: application/json\n    { \"id\": 2, \"author_id\": 1, \"title\": \"Pride and Prejudice\", \"author\": { \"id\": 1, \"first_name\": \"Jane\", \"last_name\": \"Austen\" } }\n\nEmbeds are defined by the query, they require no setup in the database.\n\n## Development\n\n```sh\n# Install dependencies\nmake install\n\n# Run the demo with MSW\nmake run-msw\n\n# Run the demo with fetch-mock\nmake run-fetch-mock\n\n# Run the demo with sinon\nmake run-sinon\n\n# Run tests\nmake test\n\n# Build minified version\nmake build\n```\n\nYou can sign-in to the demo with `janedoe` and `password`\n\n## License\n\nFakeRest is licensed under the [MIT License](LICENSE), sponsored by [marmelab](http://marmelab.com).\n","funding_links":[],"categories":["Testing"],"sub_categories":["Mocking"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarmelab%2FFakeRest","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarmelab%2FFakeRest","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarmelab%2FFakeRest/lists"}