{"id":15984636,"url":"https://github.com/seriouslag/httpclient","last_synced_at":"2025-03-16T07:32:02.653Z","repository":{"id":45413614,"uuid":"416869552","full_name":"seriouslag/HttpClient","owner":"seriouslag","description":"Typed opinionated library to make HTTP calls in node and the browser.","archived":false,"fork":false,"pushed_at":"2024-04-23T17:32:46.000Z","size":2849,"stargazers_count":12,"open_issues_count":4,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-02-27T05:49:33.962Z","etag":null,"topics":["axios","browser","fetch","http","javascript","node","typescript"],"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/seriouslag.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","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":"2021-10-13T19:17:18.000Z","updated_at":"2024-04-23T17:32:49.000Z","dependencies_parsed_at":"2024-04-23T17:39:14.336Z","dependency_job_id":"867a9530-ca8e-4462-a0fc-0206dc75b64c","html_url":"https://github.com/seriouslag/HttpClient","commit_stats":{"total_commits":126,"total_committers":4,"mean_commits":31.5,"dds":0.5396825396825398,"last_synced_commit":"dc97cb780de8d8020387803329f7eb621663f02e"},"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seriouslag%2FHttpClient","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seriouslag%2FHttpClient/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seriouslag%2FHttpClient/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seriouslag%2FHttpClient/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/seriouslag","download_url":"https://codeload.github.com/seriouslag/HttpClient/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243806045,"owners_count":20350775,"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":["axios","browser","fetch","http","javascript","node","typescript"],"created_at":"2024-10-08T02:09:57.253Z","updated_at":"2025-03-16T07:32:02.261Z","avatar_url":"https://github.com/seriouslag.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003e\n  HttpClient\n\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  Typed wrapper around fetch or axios.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/seriouslag/HttpClient/actions\"\u003e\n    \u003cimg alt=\"Github - Action\" src=\"https://github.com/seriouslag/httpclient/actions/workflows/main.yml/badge.svg\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://www.npmjs.com/package/@seriouslag/httpclient\"\u003e\n    \u003cimg alt=\"NPM Package\" src=\"https://img.shields.io/npm/v/@seriouslag/httpclient\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://dev.azure.com/landongavin/nullspace/_build?definitionId=3\"\u003e\n    \u003cimg alt=\"Code Coverage\" src=\"https://img.shields.io/azure-devops/coverage/landongavin/nullspace/3/main?label=Coverage\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://sonarcloud.io/project/issues?id=seriouslag_HttpClient\"\u003e\n    \u003cimg alt=\"Sonar Violations\" src=\"https://img.shields.io/sonar/violations/seriouslag_HttpClient/main?format=long\u0026server=https%3A%2F%2Fsonarcloud.io\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  This package's API is still developing and will not follow SEMVER until release 1.0.0.\n\nHttpClient helps standardizes making HTTP calls regardless of the underlying client used, (fetch is used by default but other clients are available) and handling when errors are thrown. HttpClient works both in the browser and node environments. Exposes an interface to abort HTTP calls using \u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/API/AbortController\"\u003eAbortController\u003c/a\u003e. See below about using [AbortController](#using-abortcontroller) in older environments. Exposes an interface to control how requests and responses are handled. See below about using [HttpClient's Request Strategies](#using-request-strategies). Some strategies are provided in this package, but you can also implement your own strategies. List of strategies are provided below.\n\n\u003c/p\u003e\n\n\u003ch2\u003eInstallation\u003c/h2\u003e\n\n```bash\nnpm install @seriouslag/httpclient\n```\n\n\u003ch2\u003eExample\u003c/h2\u003e\n\n\u003cp\u003eTo see additional examples look in the `src/examples/` directory.\u003c/p\u003e\n\nBasic example:\n\n```typescript\nimport { HttpClient } from '@seriouslag/httpclient';\n\ninterface NamedLink {\n  name: string;\n  url: string;\n}\n\ninterface PokemonPage {\n  count: number;\n  next: string | null;\n  previous: string | null;\n  results: NamedLink[];\n}\n\nconst httpClient = new HttpClient();\n\nasync function fetchPokemonPage(offset: number = 0, pageSize: number = 20) {\n  const pokemonApiUrl = 'https://pokeapi.co/api/v2';\n  return await this.httpClient.get\u003cPokemonPage\u003e(`${pokemonApiUrl}/pokemon`, {\n    params: {\n      offset: offset,\n      limit: pageSize,\n    },\n  });\n}\n\n// IIFE\n(async () =\u003e {\n  const results = await fetchPokemonPage(0, 100);\n  console.log(results);\n})();\n```\n\n\u003ch2\u003eUsing axios\u003c/h2\u003e\n\nWe can use axios as the underlying client by installing the `@seriouslag/httpclient-axios` package.\nA custom client adaptor can be provided to the HttpClient constructor, an interface is exposed to allow for custom client adaptors to be created.\n\n```bash\nnpm install @seriouslag/httpclient @seriouslag/httpclient-axios\n```\n\n\u003cp\u003e\n  Axios can be configured, axios options can be passed into the constructor of HttpClient.\n\u003c/p\u003e\n\n```typescript\nimport { HttpClient } from '@seriouslag/httpclient';\nimport { AxiosClientAdaptor } from '@seriouslag/httpclient-axios';\nimport { Agent } from 'https';\n\nconst httpsAgent = new Agent({\n  rejectUnauthorized: false,\n});\n\nconst axiosClientAdaptor = new AxiosClientAdaptor({\n  httpsAgent,\n});\n\nconst httpClient = new HttpClient(axiosClientAdaptor);\n```\n\n\u003ch2\u003eUsing AbortController\u003c/h2\u003e\n\u003cp\u003eEach of the HTTP methods of the HttpClient accept an instance of a AbortController. This allows HTTP requests to be cancelled if not already resolved.\n\n```typescript\nimport { HttpClient } from '@seriouslag/httpclient';\n\ninterface PokemonPage {\n  count: number;\n  next: string | null;\n  previous: string | null;\n  results: NamedLink[];\n}\n\nconst pokemonApiUrl = 'https://pokeapi.co/api/v2';\nconst httpClient = new HttpClient();\nconst cancelToken = new AbortController();\n\nconst request = httpClient.get\u003cPokemonPage\u003e(\n  `${pokemonApiUrl}/pokemon`,\n  cancelToken,\n);\n\ncancelToken.abort();\n\ntry {\n  const result = await request;\n  console.log('Expect to not get here because request was aborted.', result);\n} catch (e) {\n  console.log('Expect to reach here because request was aborted.');\n}\n```\n\n\u003c/p\u003e\n\n\u003ch3\u003eAbortController in older environments\u003c/h3\u003e\n\u003cp\u003e\n  Abort controller is native to node 15+ and modern browsers. If support is needed for older browsers/node versions then polyfills can be found. This polyfill is used in the Jest test environment for this repository: \u003ca href=\"https://www.npmjs.com/package/abortcontroller-polyfill\"\u003eabortcontroller-polyfill\u003c/a\u003e\n\n```typescript\nimport 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only';\nimport { HttpClient } from '@seriouslag/httpclient';\n\nconst httpClient = new HttpClient();\n```\n\n\u003c/p\u003e\n\n\u003ch2\u003eUsing Request Strategies\u003c/h2\u003e\n\u003cp\u003e\nA request strategy is middleware to handle how requests are made and how responses are handled. This is exposed to the consumer using the `HttpRequestStrategy` interface. A request strategy can be passed into the HttpClient (it will be defaulted if not) or it can be passed into each request (if not provided then the strategy provided by the HttpClient will be used). A custom strategy can be provided to the HttpClient's constructor.\n\nProvided strategies:\n\n\u003cul\u003e\n  \u003cli\u003eDefaultHttpRequestStrategy - Throws when a response's status is not 2XX\u003c/li\u003e\n  \u003cli\u003eExponentialBackoffRequestStrategy - Retries requests with a backoff. Throws when a response's status is not 2XX\u003c/li\u003e\n  \u003cli\u003eMaxRetryHttpRequestStrategy - Retries requests. Throws when a response's status is not 2XX\u003c/li\u003e\n  \u003cli\u003eTimeoutHttpRequestStrategy - Requests have are canceled if a request takes longer then provided timeout. Throws when a response's status is not 2XX\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\n\n\u003ch3\u003eUsing Request Strategy in the constructor\u003c/h3\u003e\n\n\u003cp\u003eThe following code creates an instance of the HttpClient with a custom HttpRequestStrategy, all requests will now use this strategy by default.\u003c/p\u003e\n  \n```typescript\nimport { HttpClient, HttpRequestStrategy } from '@seriouslag/httpclient';\n\nclass CreatedHttpRequestStrategy implements HttpRequestStrategy {\n\n  /** Passthrough request to axios and check response is created status */\n  public async request\u003cT = unknown\u003e (client: AxiosInstance, axiosConfig: AxiosRequestConfig) {\n    const response = await client.request\u003cT\u003e(axiosConfig);\n    this.checkResponseStatus\u003cT\u003e(response);\n    return response;\n  }\n\n  /** Validates the HTTP response is successful created status or throws an error */\n  private checkResponseStatus\u003cT = unknown\u003e (response: HttpResponse\u003cT\u003e): HttpResponse\u003cT\u003e {\n    const isCreatedResponse = response.status === 201;\n    if (isCreatedResponse) {\n      return response;\n    }\n    throw response;\n  }\n}\n\nconst httpRequestStrategy = new CreatedHttpRequestStrategy();\n\n// all requests will now throw unless they return an HTTP response with a status of 201\nconst httpClient = new HttpClient({\n  httpRequestStrategy,\n});\n\n````\n\n\u003ch3\u003eUsing Request Strategy in a request\u003c/h3\u003e\n\n\u003cp\u003eThe following code creates an instance of the HttpClient with a provided HttpRequestStrategy (MaxRetryHttpRequestStrategy), then starts a request and passes a different strategy (DefaultHttpRequestStrategy) to the request. The request will now used the strategy provided instead of the HttpClients strategy.\u003c/p\u003e\n\n ```typescript\nimport { HttpClient, DefaultHttpRequestStrategy, MaxRetryHttpRequestStrategy } from '@seriouslag/httpclient';\n\nconst httpClient = new HttpClient({\n  httpRequestStrategy: new MaxRetryHttpRequestStrategy(10),\n});\n\n// IIFE\n(async () =\u003e {\n  const response = await httpClient.get('/endpoint', {\n    httpRequestStrategy: new DefaultHttpRequestStrategy(),\n  });\n})();\n````\n\n\u003c/p\u003e\n  \n\u003ch2\u003eLogging\u003c/h2\u003e\n\u003cp\u003eAn interface is exposed to the HttpClient constructor to allow a logging instance to be provided.\n  \n```typescript\nconst logger: Logger = {\n  info: (message: string, ...args: unknown[]) =\u003e console.log(message, ...args),\n  warn: (message: string, ...args: unknown[]) =\u003e console.warn(message, ...args),\n  error: (message: string, ...args: unknown[]) =\u003e console.error(message, ...args),\n  debug: (message: string, ...args: unknown[]) =\u003e console.debug(message, ...args),\n};\n  \nconst httpClient = new HttpClient({\n  logger,\n});\n```\n\n\u003c/p\u003e\n\n\u003ch2\u003eContributing\u003c/h2\u003e\n\n[Contributing](./CONTRIBUTING.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseriouslag%2Fhttpclient","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fseriouslag%2Fhttpclient","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseriouslag%2Fhttpclient/lists"}