{"id":17488638,"url":"https://github.com/daohoangson/js-tinhte-api","last_synced_at":"2026-03-09T16:05:47.669Z","repository":{"id":46304311,"uuid":"122430667","full_name":"daohoangson/js-tinhte-api","owner":"daohoangson","description":"Tinh tế API authentication for React apps","archived":false,"fork":false,"pushed_at":"2025-08-31T09:54:29.000Z","size":2178,"stargazers_count":1,"open_issues_count":6,"forks_count":7,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-10-01T17:55:25.910Z","etag":null,"topics":["api","implicit-flow","nextjs","oauth2","react","server-side-rendering","xenforo"],"latest_commit_sha":null,"homepage":"https://tinhte-api.now.sh/?client_id=2wseo5fywn","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/daohoangson.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2018-02-22T04:28:57.000Z","updated_at":"2023-05-04T12:54:02.000Z","dependencies_parsed_at":"2024-12-22T21:04:52.354Z","dependency_job_id":"69a26ac2-de53-45d1-aa98-9ba769b654b7","html_url":"https://github.com/daohoangson/js-tinhte-api","commit_stats":{"total_commits":166,"total_committers":3,"mean_commits":"55.333333333333336","dds":"0.012048192771084376","last_synced_commit":"ae63bff322e48453b415e083ce609faa2d353292"},"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"purl":"pkg:github/daohoangson/js-tinhte-api","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daohoangson%2Fjs-tinhte-api","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daohoangson%2Fjs-tinhte-api/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daohoangson%2Fjs-tinhte-api/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daohoangson%2Fjs-tinhte-api/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/daohoangson","download_url":"https://codeload.github.com/daohoangson/js-tinhte-api/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daohoangson%2Fjs-tinhte-api/sbom","scorecard":{"id":322045,"data":{"date":"2025-08-11","repo":{"name":"github.com/daohoangson/js-tinhte-api","commit":"ae63bff322e48453b415e083ce609faa2d353292"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.6,"checks":[{"name":"Code-Review","score":0,"reason":"Found 1/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":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","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":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"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":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/npm.yml:1","Info: no jobLevel write permissions found"],"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":"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":"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":"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":"License","score":0,"reason":"license file not detected","details":["Warn: project does not have a license file"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Pinned-Dependencies","score":3,"reason":"dependency not pinned by hash detected -- score normalized to 3","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/npm.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/daohoangson/js-tinhte-api/npm.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/npm.yml:20: update your workflow using https://app.stepsecurity.io/secureworkflow/daohoangson/js-tinhte-api/npm.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/npm.yml:25: update your workflow using https://app.stepsecurity.io/secureworkflow/daohoangson/js-tinhte-api/npm.yml/master?enable=pin","Warn: containerImage not pinned by hash: docker/Dockerfile:2: pin your Docker image by updating node:14.16.0 to node:14.16.0@sha256:f6b9ff4caca9d4f0a331a882e560df242eb332b7bbbed2f426784de208fcd7bd","Info:   0 out of   2 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 third-party GitHubAction dependencies pinned","Info:   0 out of   1 containerImage dependencies pinned","Info:   1 out of   1 npmCommand dependencies pinned"],"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":"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":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"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"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 12 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"94 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92","Warn: Project is vulnerable to: GHSA-hxwm-x553-x359","Warn: Project is vulnerable to: GHSA-h5c3-5r3r-rr8q","Warn: Project is vulnerable to: GHSA-rmvr-2pp2-xj38","Warn: Project is vulnerable to: GHSA-xx4v-prfh-6cgc","Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-fwr7-v2mv-hh25","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-x9w5-v3q2-3rhw","Warn: Project is vulnerable to: GHSA-w8qv-6jwh-64r5","Warn: Project is vulnerable to: GHSA-7gc6-qh9x-w6h8","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-xwcq-pm8m-c4vf","Warn: Project is vulnerable to: GHSA-w573-4hg7-7wgq","Warn: Project is vulnerable to: GHSA-434g-2637-qmqr","Warn: Project is vulnerable to: GHSA-49q7-c7j4-3p7m","Warn: Project is vulnerable to: GHSA-977x-g7h5-7qgw","Warn: Project is vulnerable to: GHSA-f7q4-pwc6-w24p","Warn: Project is vulnerable to: GHSA-fc9h-whq2-v747","Warn: Project is vulnerable to: GHSA-vjh7-7g9h-fjfh","Warn: Project is vulnerable to: GHSA-4gmj-3p3h-gm8h","Warn: Project is vulnerable to: GHSA-67mh-4wv8-2f99","Warn: Project is vulnerable to: GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-43f8-2h32-f4cj","Warn: Project is vulnerable to: GHSA-rc47-6667-2j5j","Warn: Project is vulnerable to: GHSA-78xj-cgh5-2h22","Warn: Project is vulnerable to: GHSA-2p57-rm9w-gvfp","Warn: Project is vulnerable to: GHSA-896r-f27r-55mw","Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h","Warn: Project is vulnerable to: GHSA-593f-38f6-jp5m","Warn: Project is vulnerable to: GHSA-x2rg-q646-7m2v","Warn: Project is vulnerable to: GHSA-jgmv-j7ww-jx2x","Warn: Project is vulnerable to: GHSA-76p3-8jx3-jpfq","Warn: Project is vulnerable to: GHSA-3rfm-jhwj-7488","Warn: Project is vulnerable to: GHSA-hhq3-ff78-jv3g","Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-qrpm-p2h7-hrv2","Warn: Project is vulnerable to: GHSA-mwcw-c2x4-8c55","Warn: Project is vulnerable to: GHSA-vxf5-wxwp-m7g9","Warn: Project is vulnerable to: GHSA-25mp-g6fv-mqxx","Warn: Project is vulnerable to: GHSA-c59h-r6p8-q9wc","Warn: Project is vulnerable to: GHSA-7gfc-8cq8-jh5f","Warn: Project is vulnerable to: GHSA-qpjv-v59x-3qc4","Warn: Project is vulnerable to: GHSA-w7rc-rwvf-8q5r","Warn: Project is vulnerable to: GHSA-r683-j2x4-v87g","Warn: Project is vulnerable to: GHSA-cwx2-736x-mf6w","Warn: Project is vulnerable to: GHSA-v39p-96qg-c8rf","Warn: Project is vulnerable to: GHSA-8v63-cqqc-6r2c","Warn: Project is vulnerable to: GHSA-3j8f-xvm3-ffx4","Warn: Project is vulnerable to: GHSA-4p35-cfcx-8653","Warn: Project is vulnerable to: GHSA-7f3x-x4pr-wqhj","Warn: Project is vulnerable to: GHSA-jpp7-7chh-cf67","Warn: Project is vulnerable to: GHSA-q6wq-5p59-983w","Warn: Project is vulnerable to: GHSA-j9fq-vwqv-2fm2","Warn: Project is vulnerable to: GHSA-pqw5-jmp5-px4v","Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9","Warn: Project is vulnerable to: GHSA-9wv6-86v2-598j","Warn: Project is vulnerable to: GHSA-h7cp-r72f-jxh6","Warn: Project is vulnerable to: GHSA-v62p-rq8g-8h59","Warn: Project is vulnerable to: GHSA-566m-qj78-rww5","Warn: Project is vulnerable to: GHSA-hwj9-h5mp-3pm3","Warn: Project is vulnerable to: GHSA-7fh5-64p2-3v2j","Warn: Project is vulnerable to: GHSA-hrpp-h998-j3pp","Warn: Project is vulnerable to: GHSA-p8p7-x288-28g6","Warn: Project is vulnerable to: GHSA-gcx4-mw62-g8wm","Warn: Project is vulnerable to: GHSA-cgfm-xwp7-2cvr","Warn: Project is vulnerable to: GHSA-rm97-x556-q36h","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-g4rg-993r-mgx7","Warn: Project is vulnerable to: GHSA-4rq4-32rv-6wp6","Warn: Project is vulnerable to: GHSA-64g7-mvw6-v9qj","Warn: Project is vulnerable to: GHSA-vx3p-948g-6vhq","Warn: Project is vulnerable to: GHSA-3jfq-g458-7qm9","Warn: Project is vulnerable to: GHSA-r628-mhmh-qjhw","Warn: Project is vulnerable to: GHSA-9r2w-394v-53qc","Warn: Project is vulnerable to: GHSA-5955-9wpr-37jh","Warn: Project is vulnerable to: GHSA-qq89-hq3f-393p","Warn: Project is vulnerable to: GHSA-f5x3-32g6-xq36","Warn: Project is vulnerable to: GHSA-pq67-2wwv-3xjx","Warn: Project is vulnerable to: GHSA-8cj5-5rvv-wf4v","Warn: Project is vulnerable to: GHSA-4wf5-vphf-c2xc","Warn: Project is vulnerable to: GHSA-52f5-9888-hmc6","Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3","Warn: Project is vulnerable to: GHSA-7p7h-4mm5-852v","Warn: Project is vulnerable to: GHSA-38fc-wpqx-33j7","Warn: Project is vulnerable to: GHSA-fhg7-m89q-25r3","Warn: Project is vulnerable to: GHSA-j8xg-fqg3-53r7","Warn: Project is vulnerable to: GHSA-6fc8-4gx4-v693","Warn: Project is vulnerable to: GHSA-3h5v-q93c-6h6q"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-18T01:36:50.598Z","repository_id":46304311,"created_at":"2025-08-18T01:36:50.599Z","updated_at":"2025-08-18T01:36:50.599Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30301556,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-09T14:33:48.460Z","status":"ssl_error","status_checked_at":"2026-03-09T14:33:48.027Z","response_time":61,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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","implicit-flow","nextjs","oauth2","react","server-side-rendering","xenforo"],"created_at":"2024-10-19T04:08:42.965Z","updated_at":"2026-03-09T16:05:47.646Z","avatar_url":"https://github.com/daohoangson.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# tinhte-api React Component\n\n[![build][build-badge]][build]\n[![npm package][npm-badge]][npm]\n[![Codecov][codecov-badge]][codecov]\n\nQuickly setup API authentication against [Tinh tế](https://tinhte.vn) for React applications.\nSee the [demo Next.js app](https://tinhte-api.vercel.app/?client_id=2wseo5fywn).\n\n## Usage\n\n### Install\n\nInstall the React library:\n\n```bash\nnpm install --save tinhte-api-react\n```\n\nOr install the core library only:\n\n```bash\nnpm install --save tinhte-api\n```\n\nBoth library works similarly in initialization, api calling, etc. The React library is built on top of the core one and provide React components for easy usage in React web app.\n\n### Initialize\n\n`clientId` is required for api instance to work. For React library: `callbackUrl` is also required (see below for callback explanation).\n\n```js\nimport { apiFactory } from 'tinhte-api-react'\n\nconst api = apiFactory({\n    clientId: 'clientId',\n    callbackUrl: 'http://app.domain.com/api-callback'\n})\n```\n\n### Render Page\n\nUse ApiProvider higher order component to prepare parent component. The API context will be available for all children components.\n\n```js\nimport React from 'react'\n\nconst Home = () =\u003e (\n    \u003cdiv\u003e\n        \u003cChildOne /\u003e\n        \u003cChildTwo /\u003e\n        ...\n    \u003c/div\u003e\n)\n\nconst HomeWithApi = api.ProviderHoc(Home)\n```\n\n### Render Callback\n\nThe Provider HOC will attempt to authenticate user using [OAuth2 implicit flow](https://tools.ietf.org/html/rfc6749#section-1.3.2) and that requires serving an additional page as the target redirection URL. We are going to use [React Router](https://reacttraining.com/react-router/) below but you can use anything (Next.js page, expressjs route, etc.):\n\n```js\nimport {BrowserRouter as Router, Route} from 'react-router-dom'\n\n// const api = ☝️\n\n// const HomeWithApi = ☝️\n\nconst App = () =\u003e (\n    \u003cRouter\u003e\n        \u003cdiv\u003e\n            \u003cRoute exact path='/' component={HomeWithApi} /\u003e\n            \u003cRoute path='/api-callback' component={api.CallbackComponent} /\u003e\n        \u003c/div\u003e\n    \u003c/Router\u003e\n)\n```\n\nYou can also trigger the function `processCallback` directly on the callback page, \n\n```html\n\u003cscript type=\"text/javascript\" src=\"https://unpkg.com/react@^16/umd/react.production.min.js\"\u003e\u003c/script\u003e\n\u003cscript type=\"text/javascript\" src=\"https://unpkg.com/tinhte-api-react@5/dist/tinhte-api-react.umd.js\"\u003e\u003c/script\u003e\n\u003cscript type=\"text/javascript\"\u003eTinhteApiReact.processCallback();\u003c/script\u003e\n```\n\n### Fetch from API\n\nIn children components, use `apiHoc.ApiConsumer` to prepare the `api` prop and fetch data.\nIt's recommended to only use `api` if you fetch during some user action (e.g. `onClick`), in other cases,\nimplement `apiFetches` or `apiFetchesWithAuth` and let `ApiConsumer` manages those for better performance.\n\n```js\nimport { apiHoc } from 'tinhte-api-react'\n\nconst UsersMeBase = ({ api }) =\u003e {\n    const fetch = () =\u003e api.get('users/me')\n        .then((json) =\u003e console.log(json))\n        .error(reason) =\u003e console.error(reason))\n\n    return \u003cbutton onClick={fetch}\u003eFetch\u003c/button\u003e\n}\n\nconst UsersMeComponent = apiHoc.ApiConsumer(UsersMeBase)\n```\n\nUse the newly built component anywhere under Provider HOC sub-tree and it will have `props.api` setup.\n\n```js\nconst ContainerComponent = () =\u003e (\n    \u003cUsersMeComponent /\u003e\n)\n```\n\n## References\n\n### apiFactory\n\nParams:\n\n - `config` object\n   - `apiRoot` default=`'https://tinhte.vn/appforo/index.php'`\n   - `auth` object\n     - `accessToken` default=`''`\n     - `userId` default=`0`\n   - `callbackUrl` default=`''`\n   - `clientId` default=`''`\n   - `cookiePrefix` default=`''`: it is possible to use `xf_` and the React component will **only** attempt authentication when `xf_session` or `xf_user` cookies exists -- effectively sync logged in status between server side XenForo and client side JavaScript. This requires a patched version of XenForo to work because XF sets the cookie to be http only.\n   - `debug` default=`false`\n   - `ott` default=`''`\n   - `scope` default=`'read'`\n   - `headers` default=`{}`: the headers to be sent with all requests. To delete some default header for a specific request, set it to `undefined`.\n\nReturns an `api` object.\n\nExample:\n\n```js\nimport { apiFactory } from 'tinhte-api'\n\nconst api = apiFactory({ apiRoot, clientId })\n```\n\n### api.CallbackComponent\n\n*tinhte-api-react only.*\nReturns a React component.\n\n### api.ConsumerHoc\n\n*tinhte-api-react only.*\nAlias: `apiHoc.ApiConsumer`\n\nParams:\n\n - `Component` required React component\n\nProps:\n\n - `onFetched` function\n - `onFetchedWithAuth` function\n\nReturns a higher order React component, the underlying component will receive `api` and api data as props.\nIt can use `api` directly (`api.get()`, `api.post()`, `api.batch()`, etc.)\nor it can let the HOC make the requests and benefit from multiple requests\nfrom different components being batched together automatically.\n\nCurrenty, `ApiConsumer` supports 2 types of requests: `apiFetches` and `apiFetchesWithAuth`.\nThey can be configured this way:\n\n```js\n// const ComponentBase = ...\n\nComponentBase.apiFetches = {\n  someKey: {\n    method: 'GET',\n    headers: [],\n    params: {},\n    uri: 'some-uri',\n    success: (json) =\u003e json.jsonKey,\n    error: (reason) =\u003e someDefaultValue\n  },\n\n  postById: (api, { postId }) =\u003e {\n    if (!postId) {\n      return null\n    }\n\n    return {\n      uri: 'posts',\n      params: {\n        post_id: postId\n      }\n    }\n  }\n}\n\nComponentBase.apiFetchesWithAuth = {\n  visitorThreads: (api) =\u003e {\n    const userId = api.getUserId()\n    if (!userId) {\n      return null\n    }\n\n    return {\n      uri: 'threads',\n      params: {\n        creator_user_id: userId\n      }\n    }\n  }\n}\n\nconst Component = api.ConsumerHoc(ComponentBase)\n```\n\nThe ones declared in `apiFetches` will be fetched as soon as the parent `ApiProvider` is mounted.\nWhile the ones in `apiFetchesWithAuth` wait until authentication complete before being fetched.\nPlease note that it's not guaranteed that fetches in `apiFetchesWithAuth` will have a valid token (a non-logged in user visit your app for example).\nEach fetch can also be configured with a `function`, it will receive `api` and `props` as params and must return a valid object with `uri`, `method`, etc. for the fetch to work.\n\nThe HOC will do the fetches and pass data as props for the component to use (in the example above, `props.someKey` will become available).\nBy default, the HOC will use the response `JSON` object as the value, you can do some normalization via `success` to make it easier to render.\nAdditionally, you can specify the `error` callback to provide some default value in case of a failed request.\n\n### api.ProviderHoc\n\n*tinhte-api-react only.*\nParams:\n\n - `Component` required React component\n\nProps:\n\n - `apiConfig` object\n - `apiData` object\n\nReturns a higher order React component, the underlying component will receive no additional props.\n\nThe props are completely optional and using them is considered advanced usage (yes, they are a bit complicated).\nThe general use case for these props are server side rendering, you can safely ignore them for client only app\n(if you use `apiFetches` and `apiFetchesWithAuth`, it will be very easy to add SSR to your app later). A few examples:\n\n - **You need to pass the one time token from the server to client?** You may generate OTT per request and put it into `apiConfig`:\n ```js\n const ott = api.generateOneTimeToken(clientSecret)\n\n return \u003cApiProvider apiConfig={{ott}} /\u003e\n ```\n - **You want to render components on the server but they need api data to work?** You may use `fetchApiDataForProvider`, it will give you the `apiData` object, ready to be use with `ApiProvider`.\n\n### api.fetchApiDataForProvider\n\nParams:\n\n - `rootElement` required React root element\n\nReturns a `Promise` that will resolve to the usable ApiProvider props object.\n\nExample for Next.js:\n\n```js\nconst ApiProvider = api.ProviderHoc(Page)\n\nApiProvider.getInitialProps = async () =\u003e {\n  // clone api instance for security reason\n  const reqApi = api.clone()\n  const ReqApiProvider = reqApi.ProviderHoc(Page)\n  \n  const apiConfig = {}\n  if (!process.browser) {\n    // client secret should be accessible on server only\n    apiConfig.ott = reqApi.generateOneTimeToken(clientSecret)\n  }\n\n  const apiData = await api.fetchApiDataForProvider(\u003cReqApiProvider apiConfig={apiConfig} /\u003e)\n  return {apiConfig, apiData}\n}\n```\n\n### api.fetchOne\n\nAlias:\n\n - `api.del`\n - `api.get`\n - `api.post`\n - `api.put`\n\nParams:\n \n - `options` object or string (will be used as `uri`)\n   - `body` default=`undefined`\n   - `keepalive` default=`false`\n   - `method` default=`'GET'`\n   - `headers` default=`{}`\n   - `params` default=`{}`\n   - `parseJson` default=`true`\n   - `uri` default=`''`\n\nReturns a `Promise` that will resolve to the parsed json or raw response object.\n\nA GET request example:\n\n```js\napi.get('users/me')\n    .then((json) =\u003e console.log('success', json.user))\n    .catch((reason) =\u003e console.warn('error', reason))\n```\n\nExample with a POST request:\n\n```js\napi.post({\n  uri: 'threads',\n  params: {\n    forum_id: 2,\n    thread_title: 'Hello',\n    post_body: 'World.'\n  }\n})\n```\n\nExample with a GET request with headers:\n```js\napi.get({\n  uri: 'threads',\n  headers: { \n    'foo': 'bar',\n    'foo1': 'bar1',\n  }\n})\n```\n\n#### Helpers\n\n- `api.login(clientSecret: string, username: string, password: string): Promise` executes POST request to oauth/token with OAuth2 [grant_type=password](https://oauth.net/2/grant-types/password/)\n- `api.refreshToken(clientSecret: string, refreshToken: string): Promise` executes POST request to oauth/token with OAuth2 [grant_type=refresh_token](https://oauth.net/2/grant-types/refresh-token/)\n\n### api.fetchMultiple\n\nAlias:\n\n - `api.batch`\n\nParams:\n\n - `fetches` required func\n - `options` object\n   - `triggerHandlers` default=`true`\n\nReturns a `Promise` that will resolve to the response `JSON` object.\n\nExample:\n\n```js\napi.batch(() =\u003e {\n    api.get('notifications')\n        .then((json) =\u003e console.log('notifications', json.notifications))\n        .catch((reason) =\u003e console.warn('notifications error', reason))\n\n    api.get('conversations')\n        .then((json) =\u003e console.log('conversations', json.conversations))\n        .catch((reason) =\u003e console.warn('conversations error', reason))\n})\n```\n\n### api.generateOneTimeToken\n\nParams:\n\n - `clientSecret` string\n - `ttl` number|Date\n\nReturns a string.\n\n**Note:** This method will not work in browser unless debugging is turned on (`debug=true`).\nIt is strongly recommended against exposing client secret to visitors.\n\n### api.getAccessToken\n\nReturns the authenticated access token or empty string.\n\n### api.getApiRoot\n\nReturns the configured API root string.\n\n### api.getAuth\n\nReturns the authentication object.\n\n### api.getCallbackUrl\n\nReturns the configured callback URL string.\n\n### api.getClientId\n\nReturns the configured client ID string.\n\n### api.getCookiePrefix\n\nReturns the configured cookie prefix.\n\n### api.getDebug\n\nReturns `true` if debugging is turned on, or `false` otherwise.\n\n### api.getOtt\n\nReturns the configured one time token string.\n\n### api.getScope\n\nReturns the configured scope string.\n\n### api.getFetchCount\n\nReturns the number of fetches have been made since initialization.\n\n### api.getUniqueId\n\nReturns the unique ID string of this api instance.\n\n### api.getUserId\n\nReturns the authenticated access token or `0`.\n\n### api.hasAuth\n\nReturns `true` if authentication has been set, or `false` otherwise.\n\n### api.onAuthenticated\n\n*tinhte-api-react only.*\nParams:\n\n - `callback` required func\n\nReturns a `func` that can be used to cancel the callback.\nIt's recommended to use `apiFetchesWithAuth` instead of using this method directly.\n\n### api.onProviderMounted\n\n*tinhte-api-react only.*\nParams:\n\n - `callback` required function\n\nReturns a `function` that can be used to cancel the callback.\nIt's recommended to use `apiFetches` instead of using this method directly.\n\n### Setters\n\n - `api.clone(newConfig)`\n - `api.updateConfig(newConfig)`\n\n### crypt\n\n```js\nimport { crypt } from 'tinhte-api'\n\nconst { algos, encrypt, decrypt } = crypt\nconst algo = algos[0] // aes128\nconst data = 'data'\nconst key = 'key'\n\nconst encrypted = encrypt(algo, data, key)\nconst decrypted = decrypt(algo, encrypted, key)\nassert(encrypted === decrypted)\n```\n\n### standardizeReqOptions\n\n```js\nimport { standardizeReqOptions } from 'tinhte-api'\n\nconst method = 'POST'\nconst params = { q: 'foo' }\nconst uri = 'search'\nconst uniqueId = standardizeReqOptions({ method, params, uri })\n```\n\n[build-badge]: https://github.com/daohoangson/js-tinhte-api/actions/workflows/npm.yml/badge.svg\n[build]: https://github.com/daohoangson/js-tinhte-api/actions/workflows/npm.yml\n\n[npm-badge]: https://img.shields.io/npm/v/tinhte-api.svg?style=flat-square\n[npm]: https://www.npmjs.org/package/tinhte-api\n\n[codecov-badge]: https://img.shields.io/codecov/c/github/daohoangson/js-tinhte-api.svg?style=flat-square\n[codecov]: https://codecov.io/gh/daohoangson/js-tinhte-api\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdaohoangson%2Fjs-tinhte-api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdaohoangson%2Fjs-tinhte-api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdaohoangson%2Fjs-tinhte-api/lists"}