{"id":15694100,"url":"https://github.com/izelnakri/memserver","last_synced_at":"2025-07-20T12:33:08.304Z","repository":{"id":26298483,"uuid":"107869345","full_name":"izelnakri/memserver","owner":"izelnakri","description":"JS http mock server AND ORM/in-memory/in-js-vm DB you can run in-browser and node environments. Extremely useful library for fast frontend tests, rapid prototyping and single-file SPA demo deployments.","archived":false,"fork":false,"pushed_at":"2023-03-10T11:57:06.000Z","size":4916,"stargazers_count":7,"open_issues_count":14,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-07-09T04:58:01.256Z","etag":null,"topics":["cli","frontend","memory-database","mock-server","orm","spa-demo-deployments","testing","typescript"],"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/izelnakri.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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":"2017-10-22T13:42:29.000Z","updated_at":"2021-06-14T03:47:46.000Z","dependencies_parsed_at":"2024-10-09T13:45:45.698Z","dependency_job_id":null,"html_url":"https://github.com/izelnakri/memserver","commit_stats":{"total_commits":130,"total_committers":3,"mean_commits":"43.333333333333336","dds":0.08461538461538465,"last_synced_commit":"3db14525db59923d6433322190dfd392c68e24cb"},"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"purl":"pkg:github/izelnakri/memserver","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/izelnakri%2Fmemserver","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/izelnakri%2Fmemserver/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/izelnakri%2Fmemserver/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/izelnakri%2Fmemserver/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/izelnakri","download_url":"https://codeload.github.com/izelnakri/memserver/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/izelnakri%2Fmemserver/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266127212,"owners_count":23880420,"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":["cli","frontend","memory-database","mock-server","orm","spa-demo-deployments","testing","typescript"],"created_at":"2024-10-03T18:52:25.565Z","updated_at":"2025-07-20T12:33:08.289Z","avatar_url":"https://github.com/izelnakri.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"![docker-based-ci](https://github.com/izelnakri/memserver/workflows/docker-based-ci/badge.svg)\n\n# What is MemServer?\nMemServer is an in-memory database/ORM and http mock server you can run in-browser and node environments.\nExtremely useful library for fast frontend tests, rapid prototyping, single-file SPA demo deployments.\n\n## Installation\nIn order to use memserver CLI you need to have typescript set up in your project folder.\n`memserver` binary will only work on typescript project directories since it uses ts-node under the hood for `memserver console` and `memserver g fixtures $modelName` generation commands.\n\n``` npm install -g @memserver/cli ```\n\n``` memserver ```\n\nYou can use the CLI to create relevant boilerplate files and initial setup\n\n### Memserver Model API\n\n```js\n// MEMSERVER MODEL API\nimport Model from '@memserver/model';\n// OR:\nconst Model = require('@memserver/model').default;\n// THEN:\n\nclass User extends Model {\n // NOTE: you can add here your static methods\n static serializer(modelOrArray) {\n   return modelOrArray;\n }\n};\n// allows User.serializer(user);\n\nUser.findAll(); // [];\n\nUser.insert({ firstName: 'Izel', lastName: 'Nakri' }); // { id: 1, firstName: 'Izel', lastName: 'Nakri' }\n\nlet usersAfterInsert = User.findAll(); // [{ id: 1, firstName: 'Izel', lastName: 'Nakri' }]\n\nlet insertedUser = usersAfterInsert[0];\n\ninsertedUser.firstName = 'Isaac';\n\nUser.findAll(); // [{ id: 1, firstName: 'Izel', lastName: 'Nakri' }]\n\nUser.update(insertedUser); // { id: 1, firstName: 'Isaac', lastName: 'Nakri' }\n\nUser.findAll(); // [{ id: 1, firstName: 'Isaac', lastName: 'Nakri' }]\n\nlet updatedUser = User.find(1); // { id: 1, firstName: 'Isaac', lastName: 'Nakri' }\n\nlet anotherUser = User.insert({ firstName: 'Brendan' }); // { id: 2, firstName: 'Brendan', lastName: null }\n\nupdatedUser.firstName = 'Izel';\n\nUser.findAll(); // [{ id: 1, firstName: 'Isaac', lastName: 'Nakri' }, { id: 2, firstName: 'Brendan', lastName: null }]\n\nUser.delete(updatedUser); // { id: 1, firstName: 'Isaac', lastName: 'Nakri' }\n\nUser.findAll(); // [{ id: 2, firstName: 'Brendan', lastName: null }]\n```\n\nNOTE: API also works for UUIDs instead of id primary keys\n\n### Memserver Server API\n\n```js\n\n// in memserver/routes.ts:\n\nimport User from './models/user';\nimport Response from '@memserver/response';\n\ninterface Request {\n  headers: object,\n  params: object,\n  queryParams: object,\n  body: object\n}\n\nexport default function() {\n  this.logging = true; // OPTIONAL: only if you want to log incoming requests/responses\n  this.urlPrefix = 'http://localhost:8000/api'; // OPTIONAL: if you want to scope all the routes under a host/url\n\n  this.post('/users', (request: Request) =\u003e {\n    const user = User.insert(request.params.user);\n\n    return { user: User.serializer(user) };\n  });\n\n  // OR:\n  this.post('/users', User);\n\n  this.get('/users', (request: Request) =\u003e {\n    if (request.queryParams.filter === 'is_active') {\n      const users = User.findAll({ is_active: true });\n\n      return { users: User.serializer(users) };\n    }\n\n    return Response(422, { error: 'filter is required' });\n  });\n\n  // Shorthand without filter, displaying all users: this.get('/users', User);\n\n  this.get('/users/:id', (request: Request) =\u003e {\n    return { user: User.serializer(User.find(request.params.id)) };\n    // NOTE: you can wrap it with auth through custom User.findFromHeaders(request.headers) if needed.\n  });\n\n  // OR:\n  this.get('/users/:id', User);\n\n  this.put('/users/:id', (request: Request) =\u003e {\n    let user = User.find(request.params.id);\n\n    if (!user) {\n      return Response(404, { error: 'user not found');\n    }\n\n    return { user: User.serializer(User.update(user.params.user)) };\n  });\n\n  // OR:\n  this.put('/users/:id', User);\n\n  this.delete('/users/:id', ({ params }) =\u003e {\n    const user = User.find(params.id);\n\n    if (!user) {\n      return Response(404, { errors: 'user not found' });\n    }\n\n    return User.delete(user);\n  });\n\n  // OR:\n  this.delete('/users/:id', User);\n\n  // You can also mock APIs under different hostname\n\n  this.get('https://api.github.com/users/:username', (request) =\u003e {\n    // NOTE: your mocking logic\n  });\n\n  // OTHER OPTIONS:\n\n  this.passthrough('https://api.stripe.com');\n  // OR: this.passthrough('https://somedomain.com/api');\n\n  // OPTIONAL: this.timing(500); if you want to slow down responses for testing something etc.\n  // BookRoutes.apply(this); // if you want to apply routes from a separate file\n}\n```\n\nYou can also add routes on demand for your tests:\n\n```ts\nimport Server from './memserver/index';\nimport Response from '@memserver/response';\n\ntest('testing form submit errors when backend is down', async function (assert)  {\n\n  Server.post('/users'. (request) =\u003e {\n    return Response(500, {});\n  });\n\n  // NOTE: also there is Server.get, Server.update, Server.delete, Server.put for mocking with those verbs\n\n  await visit('/form');\n\n  // submit the form\n  // POST /users will be added to your route handlers or gets overwritten if it exists\n});\n```\n\n### Memserver init/shutdown API\n\n```ts\n// in memserver/index.ts:\n\nimport Memserver from \"@memserver/server\";\nimport initializer from \"./initializer\";\nimport routes from \"./routes\";\n\nconst MemServer = new Memserver({\n  initializer: initializer,\n  routes: routes\n});\n\nexport default MemServer;\n\n// If you want to shutdown request mocking: MemServer.shutdown();\n// If you want to reset a database with predefined data:\n// User.resetDatabase([{ id: 1, firstName: 'Izel', lastName: 'Nakri' }, { id: 2, firstName: 'Brendan', lastName: 'Eich' }]);\n```\n\nThis is basically a superior mirage.js API \u0026 implementation. Also check the tests...\n\n### Memserver serializer API:\n\nMemserver serializer is very straight-forward, performant and functional/explicit. We have two ways to serialize model\ndata, it is up to you the developer if you want to serialize it in a custom format(for example JSONAPI) by adding a new\nstatic method(`static customSerializer(modelOrArray) {}`) on the model:\n\nMemserver serializer API:\n\n```js\nimport Model from '@memserver/model';\n\nclass User extends Model {\n}\n\nconst user = User.find(1);\n\nconst serializedUserForEndpoint = { user: User.serializer(user) }; // or User.serialize(user);\n\nconst users = User.findAll({ active: true });\n\nconst serializedUsersForEndpoint = { users: User.serializer(users) }; // or users.map((user) =\u003e User.serialize(user));\n```\n\nCustom serializers:\n\n```js\nimport Model from '@memserver/model';\n\nclass User extends Model {\n  static customSerializer(modelObjectOrArray) {\n    if (Array.isArray(objectOrArray)) {\n      return modelObjectOrArray.map((object) =\u003e this.serialize(object));\n    }\n\n    return this.customSerialize(objectOrArray);\n  }\n\n  static customSerialize(object) {\n    return Object.assign({}, object, {\n      newKey: 'something'\n    });\n  }\n}\n\nconst user = User.find(1);\n\nconst serializedUserForEndpoint = { user: User.customSerializer(user) }; // or User.customSerialize(user);\n\nconst users = User.findAll({ active: true });\n\nconst serializedUsersForEndpoint = { users: User.customSerializer(users) }; // or users.map((user) =\u003e User.customSerialize(user));\n```\n\n### Why this is superior to Mirage?\n\n- Data stored as pure JS objects and their Model module methods provides a functional way to work on the data.\nThis makes the inserts, updates faster and encourages a better programming model.\n\n- Better typecasting on submitted JSON data and persisted models. Empty string are `null`, '123' is a JS number, integer foreign key constraints are not strings.\n\n- `@memserver/response` does not require `new Reponse`, just `Response`.\n\n- Less code output and dependencies.\n\n- No bad APIs such as association(). Better APIs, no strange factory API that introduces redundant concepts as traits,\nor implicit association behavior. Your model inserts are your factories. You can easily create different ES6 standard\nmethods on the model modules, thus memserver is easier and better to extend.\n\n- No implicit model lifecycle callbacks such as `beforeCreate`, `afterCreate`, `afterUpdate`, `beforeDelete` etc.\nThis is an old concept that is generally deemed harmful for development, we shouldn't do that extra computation in our\nruntimes. Autogenerating things after a model gets created is an implicit thus bad behavior. Validations could be done\nin future as types or TS type decorators(like `class-validator` npm package), thus validations don't need to  happen in\nruntime and all would be check during development via typescript typecheck/linting.\n\n- No implicit relationship tracking, creating and updating a relationship should be done on the foreign-key of the\nmodels relationship. This might seem verbose, but leads to faster runtime and in future might allow better\ntypechecking/annotated validations on model properties. Due to unresolved nature of this situation we leave implicit\nrelationship settings. Instead users should set the related foreign keys and change that value to relationship\nprimary key just as it would have been done on a SQL database.\n\n- No implicit/custom serializer abstraction/API. Memserver model serializer is more sane than mirage. It makes it easier\nand more functional to create your own serializers since all `MemserverModel.serializer(modelOrArray)` does is, it embeds\ndefined `embedReferences`s and sets undefined or null values to null in the resulted objectOrArray.\n\n- route shorthands accept the model definition to execute default behavior: `this.post('/users', User)` doesn't need to dasherize,\nunderscore or do any other string manipulation to get the reference model definition. It also returns correct default\nhttp status code based on the HTTP verb, ex. HTTP POST returns 201 Created just like mirage.\n\n- very easy to debug/develop the server, serialize any data in a very predictable and functional way.\n\n- API is very similar to Mirage, it can do everything mirage can do, while all redudant and worse API removed.\n\n- can run on node.js thus allows frontend mocking on server-side rendering context.\n\n- written in Typescript, thus provides type definitions by default.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fizelnakri%2Fmemserver","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fizelnakri%2Fmemserver","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fizelnakri%2Fmemserver/lists"}