{"id":22122609,"url":"https://github.com/thorgate/tg-resources","last_synced_at":"2025-07-25T14:31:04.499Z","repository":{"id":1207079,"uuid":"41436836","full_name":"thorgate/tg-resources","owner":"thorgate","description":"Abstractions on-top of fetch/superagent (or other Ajax libaries) for communication with REST.","archived":false,"fork":false,"pushed_at":"2024-11-20T15:59:18.000Z","size":2441,"stargazers_count":5,"open_issues_count":9,"forks_count":5,"subscribers_count":18,"default_branch":"master","last_synced_at":"2024-11-27T22:05:04.251Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://thorgate.github.io/tg-resources/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thorgate.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-08-26T16:29:36.000Z","updated_at":"2024-11-20T15:59:22.000Z","dependencies_parsed_at":"2024-06-18T22:44:05.607Z","dependency_job_id":"1ca0979c-9d8a-4722-bbdb-74b6ef8c1000","html_url":"https://github.com/thorgate/tg-resources","commit_stats":{"total_commits":242,"total_committers":11,"mean_commits":22.0,"dds":0.7066115702479339,"last_synced_commit":"72f0c06e01985d131b2a7d2f0680f814ed53224c"},"previous_names":[],"tags_count":34,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thorgate%2Ftg-resources","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thorgate%2Ftg-resources/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thorgate%2Ftg-resources/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thorgate%2Ftg-resources/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thorgate","download_url":"https://codeload.github.com/thorgate/tg-resources/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":227584197,"owners_count":17789665,"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-12-01T15:26:42.423Z","updated_at":"2024-12-01T15:26:43.114Z","avatar_url":"https://github.com/thorgate.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# tg-resources\n\n[![NPM version][npm-image]][npm-url]\n[![Documentation][docs-image]][docs-url]\n[![Build Status][ci-image]][ci-url]\n[![Dependency Status][depstat-image]][depstat-url]\n[![Coverage Status][coveralls-image]][coveralls-url]\n[![Downloads][download-badge]][npm-url]\n\n\u003e Abstractions on-top of `superagent` and `fetch` (or other Ajax libraries) for communication with REST.\n\u003e Targeted mostly against `Django Rest Framework (DRF)` running on `Django` so some logic might not be applicable for\n\u003e other frameworks.\n\n## Installing\n\nUsing NPM\n\n```sh\nnpm i tg-resources\n\n# And add fetch backend\nnpm i @tg-resources/fetch\n\n# Or use superagent backend\nnpm i @tg-resources/superagent\n```\n\nOr using Yarn\n\n```sh\nyarn add tg-resources\n\n# And fetch backend\nyarn add @tg-resources/fetch\n\n# Or use superagent backend\nyarn add @tg-resources/superagent\n```\n\n### Polyfills for fetch\n\nWhen you are targeting browsers without native support for fetch or running this on node versions before 17 then you need\nto provide polyfills for the fetch globals. The easiest way to do it is to add [@tg-resources/fetch-runtime](./packages/fetch-runtime#tg-resources-fetch-runtime).\n\nAlternatively you can also just use your own polyfill if you want to. In that case the methods should be available in the\nroot scope (e.g. window/self for browsers or global for node).\n\n### Does it work on react native?\n\n**YES**\n\n#### Using with hermes engine\n\nWith the version 4.0.0 hermes is fully supported as we have replaced `lodash.template` with our own url token replacement helper. :tada:\n\n#### Using `signal` with react-native\n\nUse [abortcontroller-polyfill](https://github.com/mo/abortcontroller-polyfill) until https://github.com/facebook/react-native/issues/18115 is resolved in react-native core. The polyfill does not actually close the connection, but instead ensures the fetch rejects the promise with `AbortError`. To use the polyfill add the following to the top of your app entrypoint:\n\n```\nimport 'abortcontroller-polyfill/dist/polyfill-patch-fetch'\n```\n\n## Basic Usage\n\n```js\nimport { Router } from \"tg-resources\"\nimport { FetchResource: Resource } from \"@tg-resources/fetch\"\n\nconst onLoad = result =\u003e console.log(result);\nconst onError = result =\u003e console.error(result);\n\n\nconst api = new Router({\n    cats: new Resource('/cats'),\n    cat: new Resource('/cats/${pk}')\n}, {\n    apiRoot: '/api/v1' // Set api root\n});\n\nconst apiRouter = createRouter({\n    cats: '/cats',\n    cat: '/cats/${pk}',\n}, {\n    apiRoot: '/api/v1', // Set api root\n}, Resource);\n\n// Do a get request to /api/v1/cats?gender=M\napi.cats.fetch(null, {gender: 'M'}).then(onLoad, onError);\napiRouter.cats.fetch(null, {gender: 'M'}).then(onLoad, onError);\n\n// Do a head request to /api/v1/cats?gender=M\napi.cats.head(null, {gender: 'M'}).then(onLoad, onError);\napiRouter.cats.head(null, {gender: 'M'}).then(onLoad, onError);\n\n// Do a post request to /api/v1/cats with data: {name: 'Twinky', gender: 'M'}\napi.cats.post(null, {name: 'Twinky', gender: 'M'}).then(onLoad, onError);\napiRouter.cats.post(null, {name: 'Twinky', gender: 'M'}).then(onLoad, onError);\n\n// Do a patch request to /api/v1/cats/1 with data: {name: 'Tinkelberg'}\napi.cat.patch({pk: 1}, {name: 'Tinkelberg'}).then(onLoad, onError);\napiRouter.cat.patch({pk: 1}, {name: 'Tinkelberg'}).then(onLoad, onError);\n\n// Do a put request to /api/v1/cats with data: {pk: 1, name: 'Twinky'}\napi.cats.put(null, {pk: 1, name: 'Twinky', gender: 'M'}).then(onLoad, onError);\napiRouter.cats.put(null, {pk: 1, name: 'Twinky', gender: 'M'}).then(onLoad, onError);\n\n// Do a delete request to /api/v1/cats/1 with data: {'free':'yes'}\napi.cat.del({pk: 1}, {free: 'yes'}).then(onLoad, onError);\napiRouter.cat.del({pk: 1}, {free: 'yes'}).then(onLoad, onError);\n```\n\nPlease note that the router is useful for providing default configuration and grouping\nendpoints. It's still possible to use Resources without a router(see [Resource api](#resource-api))\n\n## Configuration\n\n-   `apiRoot` _(String)_: Base for all resource paths\n-   `headers` _(Object|Function: Object)_: Optional Function or Object which can be used to add any additional headers to requests.\n-   `cookies` _(Object|Function)_: Optional Function or Object which can be used to add any additional cookies to requests. Please note\n    that in modern browsers this is disabled due to security concerns.\n-   `mutateResponse` _(Function)_: Optional function with signature `(responseData, rawResponse: ResponseWrapper, resource: Resource, requestConfig: Object) =\u003e responseData`\n    which can be used to mutate response data before resolving it. E.g. This can be used to provide access to raw\n    response codes and headers to your success handler.\n-   `mutateError` _(Function)_: Optional function with signature `(error: ResourceErrorInterface, rawResponse: ResponseWrapper, resource: Resource, requestConfig: Object) =\u003e error`\n    which can be used to mutate errors before rejecting them. E.g. This can be used to provide access to raw response codes\n    and headers to your error handler.\n-   `statusSuccess` _(Array[int]|number)_: Array (or a single value) of status codes to treat as a success. Default: [200, 201, 204]\n-   `statusValidationError` _(Array[int]|number)_: Array (or a single value) of status codes to treat as ValidationError. Default: [400]\n-   `defaultAcceptHeader` _(String)_: Default accept header that is automatically added to requests (only if `headers.Accept=undefined`). Default:\n    `'application/json'`\n-   `parseErrors` _(Function)_: Function with signature `(errorText, parentConfig) =\u003e [nonFieldErrors, errors]` which is used to parse response\n    errors into a ValidationError object. The default handler is built for Django/DRF errors.\n-   `prepareError` _(Function)_: Function with signature `(err, parentConfig) =\u003e mixed` which is used to normalize a single error. The default\n    handler is built for Django/DRF errors.\n-   `mutateRawResponse` _(Function)_: **Advanced usage:** Optional function with signature `(rawResponse: ResponseWrapper, requestConfig: Object) =\u003e rawResponse` which can be\n    used to mutate the response before it is resolved to `responseData` or a `ResourceErrorInterface` subclass. Use the\n    source of `ResponseWrapper`, `SuperagentResponse` and `Resource::ensureStatusAndJson` for guidance.\n-   `withCredentials` _(bool)_: Allow request backend to send cookies/authentication headers, useful when using same API for server-side rendering.\n-   `allowAttachments` _(bool)_: Allow POST like methods to send attachments.\n-   `signal`: _(AbortSignal)_: Pass in an [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) object to abort the request when desired. **Only supported via request config.** Default: [null]. For react-native a [polyfill](#signal-rn) is needed.\n\n## Error handling\n\nWith tg-resources, all errors are Rejected. The logic is best described with an example:\n\n```js\nconst resource = new Resource('user/login');\n\nconst errorHandler = (error) =\u003e {\n    // Network error occurred\n    if (error.isNetworkError) {\n        console.error({\n            type: 'NETWORK_FAILED',\n            error,\n        });\n    } else if (error.isAbortError) {\n        // Request was aborted\n        console.error({\n            type: 'ABORTED',\n            error,\n        });\n    } else if (error.isValidationError) {\n        // Validation error occurred (e.g.: wrong credentials)\n        console.error({\n            type: 'VALIDATION_ERROR',\n            error,\n        });\n    } else {\n        // As a last resort, also handle invalid response codes\n        console.error({\n            type: 'SERVER_ERROR',\n            error,\n        });\n    }\n};\n\nconst payload = {\n    user: 'foo',\n    passwrod: 'bar',\n};\n\nresource.post(null, payload).then(\n    (user) =\u003e\n        console.log({\n            type: 'LOGGED_IN',\n            data: {\n                user,\n            },\n        }),\n    errorHandler\n);\n```\n\n## API\n\n### `createRouter`\n\nCreates type-safe `Router` instance.\n\n#### Arguments\n\n1. `routes` _(Object)_: Object matching pattern `{ [key]: string | { [key]: string } }`.\n   String values are used as endpoints to create resource. For more info see [Resource API](#resource-api)\n   This can be nested, meaning new router is created for object types found.\n2. `config` _(Object)_: Object containing config for top level router. See [Configuration](#configuration)\n3. `resourceKlass` _Resource_: Resource class that implements backend. This allows any of the backends to be used when creating `Router`.\n\n### `Resource`\n\nConstruct a new resource for loading data from a single (or dynamic) endpoint\n\n#### Arguments\n\n1. `apiEndpoint` _(string)_: Endpoint used for this resource. Supports ES6 token syntax, e.g: \"/foo/bar/${pk}\"\n2. `config` _(Object)_: Object containing config for this resource. See [Configuration](#configuration)\n\n#### Tokenized endpoints\n\nThe Resource module also supports dynamic urls by supporting ES6 token syntax. Request methods\ncan then provide values as an object using the first argument `kwargs`.\n\nSo for example:\n\n```js\nnew Resource('/foo/bar/${pk}').get({ pk: 1 }).then((x) =\u003e x);\n```\n\nWould result in a GET request to `/foo/bar/1`\n\n#### Returns\n\n_(Resource)_: Returns instance of `Resource`.\n\n### `Resource.fetch`\n\nDo a get request to the resource endpoint with optional kwargs and query parameters.\n\n#### Arguments\n\n1. `kwargs=null` _(Object)_: Object containing the replacement values if the resource uses tokenized urls\n2. `query=null` _(Object|string)_: Query parameters to use when doing the request.\n3. `requestConfig=null` _(Object)_: Configuration overrides, useful when using same API for server-side rendering.\n\n### `Resource.options`\n\nAlias for `Resource.fetch(kwargs, query, requestConfig)` with `options` method.\n\n### `Resource.head`\n\nAlias for `Resource.fetch(kwargs, query, requestConfig)` with `head` method.\n\n#### Returns\n\n_(Promise)_: Returns a `Promise` that resolves to the remote result or throws if errors occur.\n\n### `Resource.post`\n\nDo a `method` request to the resource endpoint with optional kwargs and query parameters.\n\n#### Arguments\n\n1. `kwargs=null` _(Object)_: Object containing the replacement values if the resource uses tokenized urls\n1. `data=null` _(Object|string)_: Query parameters to use when doing the request.\n1. `query=null` _(Object|string)_: Query parameters to use when doing the request.\n1. `attachments=null` _(Array)_: Attachments, creates multipart request\n1. `requestConfig=null` _(Object)_: Configuration overrides, useful when using same API for server-side rendering.\n\n#### Returns\n\n_(Promise)_: Returns a `Promise` that resolves to the remote result or throws if errors occur.\n\n### `Resource.patch`\n\nAlias for `Resource.post(kwargs, data, query, attachments, requestConfig)` with `patch` method.\n\n### `Resource.put`\n\nAlias for `Resource.post(kwargs, data, query, attachments, requestConfig)` with `put` method.\n\n### `Resource.del`\n\nAlias for `Resource.post(kwargs, data, query, attachments, requestConfig)` with `del` method.\n\n### `ResourceErrorInterface`\n\nGeneric base class for all errors that can happen during requests\n\n#### Attributes\n\n-   `isNetworkError` _(bool)_: Always `false`\n-   `isInvalidResponseCode` _(bool)_: Always `false`\n-   `isValidationError` _(bool)_: Always `false`\n\n### `NetworkError`\n\nError class used for all network related errors\n\n#### Extends `ResourceErrorInterface` and overwrites:\n\n-   `isNetworkError` _(bool)_: Always `true`\n\n#### Attributes\n\n-   `error` _(Error)_: Original Error object that occured during network transport\n\n### `AbortError`\n\nError class used when a request is aborted\n\n#### Extends `ResourceErrorInterface` and overwrites:\n\n-   `isAbortError` _(bool)_: Always `true`\n\n#### Attributes\n\n-   `error` _(Error)_: Original Error object that was raised by the request engine\n\n### `InvalidResponseCode`\n\nError class used when unexpected response code occurs\n\n#### Extends `ResourceErrorInterface` and overwrites:\n\n-   `isInvalidResponseCode` _(bool)_: Always `true`\n\n#### Attributes\n\n-   `statusCode` _(string)_: Response status code\n-   `responseText` _(int)_: Response body text\n\n### `RequestValidationError`\n\nError class used when backend response code is in `config.statusValidationError`.\n\n#### Extends `InvalidResponseCode` and overwrites:\n\n-   `isInvalidResponseCode` _(bool)_: Always `false`\n-   `isValidationError` _(bool)_: Always `true`\n\n#### Attributes\n\n-   `errors`: _(ValidationErrorInterface|any)_: The result from `requestConfig.parseError`\n\n### `ValidationErrorInterface`\n\nError types returned by the default error parser.\n\nSupports iteration (map/forEach/for .. of/etc)\n\n#### Attributes\n\n-   `errors`: _(any)_: Errors and error messages.\n\n#### Types\n\nSince DRF errors can be arbitrarily nested and one field can have multiple\nerrors, some specific types of interest:\n\n-   `SingleValidationError`: Errors for a single field\n    the `.errors` attribute is a list of strings.\n-   `ValidationError`: Errors for an object, `.errors` is an object with field names as keys.\n-   `ListValidationError`: Errors related to list of objects. `.errors` is a list of `ValidationErrorInterface`.\n\n#### Methods\n\n(\\*) Not applicable to SingleValidationError\n\n##### `hasError`\n\n###### Returns\n\n_(bool)_: True if there are any errors.\n\n##### `getError`\\*\n\nGet field specific error\n\n###### Arguments\n\n1. `fieldName` _(Array\u003cstring\u003e|string)_: Field name or path to child error, e.g `['parent', 'child']` or array indexes for `ListValidationError`\n2. `[allowNonField=false]` _(bool)_: If true, also check for nonFieldErrors if the specified field does not have an error\n\n###### Returns\n\n_(any)_: Returns a normalized error for `fieldName` or `null`\n\n##### `firstError`\\*\n\nGet first error normalized to a string for this ValidationError\n\n###### Arguments\n\n1. `[allowNonField=false]` _(bool)_: If true, also check for nonFieldErrors\n\n###### Returns\n\n_(any)_: First error as a `string` or `null`\n\n## License\n\nMIT © [Thorgate](http://github.com/thorgate)\n\n[npm-url]: https://npmjs.org/package/tg-resources\n[npm-image]: https://img.shields.io/npm/v/tg-resources.svg?style=flat-square\n[ci-url]: https://github.com/thorgate/tg-resources/actions/workflows/run-tests.yml\n[ci-image]: https://github.com/thorgate/tg-resources/actions/workflows/run-tests.yml/badge.svg\n[depstat-url]: https://libraries.io/npm/tg-resources/\n[depstat-image]: https://img.shields.io/librariesio/github/thorgate/tg-resources\n[coveralls-url]: https://coveralls.io/github/thorgate/tg-resources?branch=master\n[coveralls-image]: https://coveralls.io/repos/github/thorgate/tg-resources/badge.svg?branch=master\n[download-badge]: http://img.shields.io/npm/dm/tg-resources.svg?style=flat-square\n[docs-image]: https://img.shields.io/badge/docs-latest-brightgreen.svg?style=flat-square\n[docs-url]: https://thorgate.github.io/tg-resources/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthorgate%2Ftg-resources","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthorgate%2Ftg-resources","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthorgate%2Ftg-resources/lists"}