{"id":21575556,"url":"https://github.com/rangle/hackstack","last_synced_at":"2025-09-12T22:44:47.086Z","repository":{"id":28578609,"uuid":"32096523","full_name":"rangle/hackstack","owner":"rangle","description":"A library for building AngularJS apps with broken APIs that are delivered late.","archived":false,"fork":false,"pushed_at":"2022-07-12T21:19:09.000Z","size":541,"stargazers_count":32,"open_issues_count":13,"forks_count":8,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-03-24T14:13:27.058Z","etag":null,"topics":[],"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/rangle.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":"2015-03-12T19:08:23.000Z","updated_at":"2024-07-08T02:34:08.000Z","dependencies_parsed_at":"2022-09-03T09:12:20.392Z","dependency_job_id":null,"html_url":"https://github.com/rangle/hackstack","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rangle%2Fhackstack","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rangle%2Fhackstack/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rangle%2Fhackstack/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rangle%2Fhackstack/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rangle","download_url":"https://codeload.github.com/rangle/hackstack/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248252690,"owners_count":21072700,"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":[],"created_at":"2024-11-24T12:13:36.638Z","updated_at":"2025-04-10T16:32:13.426Z","avatar_url":"https://github.com/rangle.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# HackStack [![Build status](https://circleci.com/gh/rangle/hackstack.svg?style=svg\u0026circle-token=4e9f2c3295779e2494abbf8fc84a8aa4f4da0c3f)](https://circleci.com/gh/rangle/hackstack)\n\n## What is HackStack?\n\n**HackStack** is an Angular module that helps you work with backend APIs that\nare incomplete or altogether missing.\n\nIn our experience working on numerous Angular projects, broken or delayed\nbackend APIs are quite common and can present a major risk if the front end\nteam cannot find a good way to react in this situation. After trying a number of\napproaches, we've found client-side mocking to the be most effective route.\n\nWe've done it a few different ways on a number of projects and presented some of\nour observations in a\n[talk](http://yto.io/slides/Building-an-AngularJS-Hack-Stack-2015.pdf) at ngConf\nin 2015. We've got a lot of positive response, but also a question: \"Why don't\nyou make this a reusable library?\" So we did. Enter HackStack.js, the library.\n\n## Installing with Bower\n\nThe easiest way to install HackStack is by using bower:\n\n```bash\n  bower install --save angular-hackstack\n```\n\nAlternatively, build HackStack using this repo.\n\n## Using HackStack\n\nTo create a new HackStack endpoint, call:\n\n```js\n  var mockEndpoint = hackstack.mock(data);\n```\n\nThis creates a fully mocked endpoint which won't make any calls to the backend\nat all. Here, `data` can be either an array of items or a path to a json file.\n\nAlternatively, you can \"wrap\" an existing endpoint: HackStack will then get the\ndata from the server and fill in the missing properties of each item based on a\nprovided template object:\n\n```js\n  var wrappedEndpoint = hackstack.wrap(endpoint, templateObject);\n```\n\n### Example\n\n```js\n  var mockEndpoint = hackstack.mock([\n    {\n      'name': 'Alice',\n      'id': 1\n    },\n    {\n      'name': 'Bob',\n      'id': 2\n    }\n    ]);\n\n  mockEndpoint.get(1)\n    .then(function (response) {\n      console.log(response.data); // logs {'name': 'Alice', 'id': 1}\n    });\n```\n\nA full example is available under the [example directory](./example)\n\n### Controlling HackStack from the Browser Console\n\nWhile you're working with HackStack, you may want to force a particular error to\nhappen on the next call to the endpoint. You can do this by exposing the mock\nendpoint object to the console and then calling `.forceError(\u003cHTTP ERROR CODE\u003e)`\non it. Subsequent requests will then return that error. Call `.forceError(null)`\nto turn this off.\n\n### Random Errors\n\nHackStack defaults to generating random errors in response to endpoint requests.\nYou can turn this off using `.disableError(true)` on your mock endpoint object.\nYou can turn it back on by calling the same method with `false`.\n\n### Artificial Delay\n\nHackStack introduces randomized artificial delay on all requests. This helps you\ndetect cases where your code makes optimistic assumptions about timing.\n\n## Assumptions\n\nThis library currently makes a couple of assumptions:\n\n* You're using AngularJS.  It's designed using AngularJS services.\n\n* You're using an abstraction factory to wrap your end points.  This service\nwill provide you an object that has methods for getting all records, getting a\nsingle record, creating a record, etc.\n\n## Architecture\n\n### `hackstack.utils`\n\nThis service provides methods that are used by both `hackstack.mock` and `hackstack.wrap`\nservices.  Those functions are:\n\n* `addErrorTrigger(errorFn, errorCode, method)`: Adds an \"error trigger\" that\n  will fire if `errorFn(response)` returns true (where `response` is the\n  response object that would otherwise be returned by HackStack) \u003cbr/\u003e\n  `errorFn` : {function} predicate that decides whether error should be returned \u003cbr/\u003e\n  `errorCode` : {integer} HTTP error code to return \u003cbr/\u003e\n  `method` : {string} Which HTTP method to check error trigger against (e.g. 'POST')\n* `disableRandomErrors(value)`: Disable random error generation. \u003cbr/\u003e\n  `value` : {boolean}\n* `forceError(errorCode)`: Reject with this error code in the next response.\n  Reset error if `errorCode` is `null`\n  \u003cbr/\u003e\n  `errorCode` : {integer}\n* `produceError(errorArray)`: Return either an error object or null depending\n  on the probability distribution defined in the errorArray \u003cbr/\u003e\n  `errorArray` : {\\[object]} (optional) an array of error objects\n* `randomError(errorArray)`: Return a random error from an array of errors\n  (`errorArray` or the default error array if none provided) \u003cbr/\u003e\n  `errorArray` : {\\[object]} (optional) an array of error objects\n* `getErrorByCode(errorCode)`: Returns an error object with error code matching\n  `errorCode`. \u003cbr/\u003e\n  `errorCode` : {integer}\n* `randomInt()`: Returns a random integer. \u003cbr/\u003e\n* `setOptions(newOptions)`: Updates the HackStack options list \u003cbr/\u003e\n  `newOptions` : {object}\n* `waitForTime()`: Returns a promise that resolves after some time. Used to\n  mimic latency \u003cbr/\u003e\n\n### `hackstack.mock`\n\n`hackstack.mock` is a service that creates a mock backend from scratch.\nTo create a HackStack instance, call `hackstack.mock(mockData, options)` where `mockData`\nis an array of objects and `options` is an optional argument of type `Object`.\n\nAlternatively, `mockData` can be the path to a JSON that is an array of objects\n\nA `hackstack.mock` object contains the following methods:\n\n* `getAll()`: Get all results (equivalent to requesting `API_BASE/endpoint/`)\n* `get(id)`: Get a single result (equivalent to requesting `API_BASE/endpoint/id`)\n* `query(queryObject)`: get the first result where for any key:value pair in\n  `queryObject`, there's a matching key:value pair in the mock data object\u003cbr/\u003e\n  `queryObject` : {object}\n* `create(object, createIdFn)`: Create a new record \u003cbr/\u003e\n  `object` : {object} \u003cbr/\u003e\n  `createIdFn` : {() -\u003e int} Function that returns an integer to be used as an id\n* `update(id, object)`: Update a record. \u003cbr/\u003e\n  `id`  : the id of the record \u003cbr/\u003e\n  `object` : {object} the object to update\n* `save(object, createIdFn)`: a method that will call create or update\n  depending on presence of an id. \u003cbr/\u003e\n  signature is identical to `create`\n\n### `hackstack.wrap`\n\n`hackstack.wrap` is a service that wraps a real backend with a local mock object.\nIt can be useful if the backend is buggy, returns incomplete data, or is yet to\nbe fully implemented.\n\nTo create a `hackstack.wrap` instance, call `hackstack.wrap(endpoint, mockObject, options)`\nwhere:\n\n* `endpoint` is a string that contains the location of the endpoint\n* `mockObject` is a single object used to complete responses from the backend\n* `options` is a an object (optional argument)\n\nNote that unlike `hackstack.mock`, you only pass a single object to `hackstack.wrap`.\nIt will use that one object to complete all of the responses your backend\nreturns by deep merging the response's properties with the objects\n\n`hackstack.wrap` also requires that you make `API_BASE` available through Angular's\ninjector. `API_BASE` should be a string that contains the base URL for your\nAPI.\n\nthe `hackstack.wrap` factory returns an object which contains the same methods as\na `hackstack.mock` object. Keep in mind however, that `hackstack.wrap` will relay all\nrequests to the backend, including `post` requests.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frangle%2Fhackstack","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frangle%2Fhackstack","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frangle%2Fhackstack/lists"}