UNPKG

@procore/core-http

Version:

A HTTP Client

276 lines (197 loc) • 10.7 kB
# `@procore/core-http` A simple HTTP client. Built on top of [up-fetch]! ```bash yarn add @procore/core-http ``` This simple wrapper over native fetch makes interacting with the Procore API a little easier. By default, it injects a CSRF token from known places when working from inside of an Micro Front End, or in a Hydra Client, but also provides customization and an extension mechanism. ## Usage <details> <summary>Simple Example</summary> ```ts import { createClient } from '@procore/core-http' const client = createClient({ errorReportingApiKey: process.env.BUGSNAG_API_KEY ?? '', }) function ExampleRequest() { client('/some-url-here') .then((data) => { // Contains the actual result. No need to call response.json() or check response.ok console.log(data) }) .catch((err) => { console.error(err) }) } ``` </details> <details> <summary>Customize options, such as <em>baseUrl</em></summary> ```ts import { createClient } from '@procore/core-http' const client = createClient({ errorReportingApiKey: process.env.BUGSNAG_API_KEY ?? '', defaults: () => ({ baseUrl: 'https://www.example.com', // uses this baseUrl for every request. }), }) function ExampleRequest() { client('/some-url-here') // request will be 'https://www.example.com/some-url-here' .then((data) => { // Contains the actual result. No need to call response.json() or check response.ok console.log(data) }) .catch((err) => { console.error(err) }) } // Or, one can override an option on a request-by-request basis: const client = createClient({ errorReportingApiKey: process.env.BUGSNAG_API_KEY ?? '', systemEvents, // pass your app's instance so the host receives events with a more accurate source }) client('/some-url-here', { baseUrl: 'https://www.example.com' }) ``` </details> ## API ### `createClient({ errorReportingApiKey, defaults?, plugins?, fetchFn?, defaultErrorHandling?, systemEvents? })` ```ts createClient({ errorReportingApiKey: string, defaults: () => RequestOptions, plugins: Array<Plugin>, fetchFn: typeof fetch, defaultErrorHandling: boolean, systemEvents: SystemEvents, }): client ``` #### `systemEvents` - optional _(default: `new SystemEvents('core-http')`)_ The event bus used to publish `UNCAUGHT_EXCEPTION` when requests fail. **Pass your app's `SystemEvents` instance** so the host receives events with a more accurate source as part of its payload when `UNCAUGHT_EXCEPTION` is dispatched. If omitted, a new instance of `SystemEvents` is created with source `core-http`. #### `defaults` - optional _(default: `() => ({})`)_ A function that returns the default request parameters that should be used on every request. It maybe be a synchronous or asynchronous function. Equivalent to the [getDefaultOptions][up] function passed to the [up][up] function in [up-fetch][up-fetch]. Refer to the [getDefaultOptions][up] API docs for all of the options. #### `plugins` - optional _(default: `[]`)_ A collection of plugins to augment the behavior of each request. Refer to the ["Plugins"](#plugins) below for more details. #### `fetchFn` - optional _(default: `fetch`)_ The function that is used to actually make the fetch calls. Equivalent to the [fetch][up] parameter in the [up][up] function in [up-fetch][up-fetch]. The default should work for most cases. The only reason to consider overriding it is perhaps for unit tests, although there are better options. #### `defaultErrorHandling` - optional _(default: `true`)_ When `true`, `createClient` will publish a `SystemEventNames.UNCAUGHT_EXCEPTION` event when a request fails. To opt out per request, pass `defaultErrorHandling: false` in request options. When `false`, `createClient` will always throw errors and will not publish the system event. #### `errorReportingApiKey` - required This value is included in the `UNCAUGHT_EXCEPTION` event payload as `errorReportingApiKey`. When it is an empty string, default error reporting is effectively disabled (exceptions are still thrown). ### `isResponseError(error: unknown): boolean` Returns `true` when the error is a response error. Exported from [up-fetch][response-error]. ```ts import { isResponseError } from '@procore/core-http' async function makeRequest() { try { // makes a core-http fetch call } catch (error: unknown) { if (isResponseError(error)) { // handle the strongly-typed response error } else { throw error // re-throw for the new handler, or handle it differently } } } ``` ### `client(url, options?)` The function returned from `createClient`. Used to make fetch requests. ```ts function upfetch( url: string | URL | Request, options?: FetcherOptions ): Promise<any> ``` #### `url` - required The request object. This is be a `string`, `URL`, or `Request` object. #### `options` - optional _(default: `{}`)_ Additional options to add to this specific request. Note that any properties provided here will override the defaults. Additional information about the available options can be found in the [up-fetch][client] docs. ### `isValidationError(error: unknown): boolean` Returns `true` when the error is a schema validation error. Exported from [up-fetch][validation-error]. ```ts import { isValidationError } from '@procore/core-http' async function makeRequest() { try { // makes a core-http fetch call } catch (error: unknown) { if (isValidationError(error)) { // handle the strongly-typed validation error } else { throw error // re-throw for the new handler, or handle it differently } } } ``` ### `request` _(deprecated)_ Makes a fetch request, with no additional handling. It is **strongly** recommended to use `createClient` instead of this method. ```ts request(url: string, requestParams: RequestParams) ``` #### `url` - required The resource to fetch #### `requestParams` - optional _(default: {})_ Used to augment the resulting `fetch` call with additional configuration, such as `method`, `baseUrl`, and `errorReportingApiKey`. - **`errorReportingApiKey`** - optional. When non-empty, enables error reporting for failed requests. Defaults to `''` (disabled; no reporting). - **`systemEvents`** - optional. Specifies which `SystemEvents` instance/event bus to use so the host receives events with a more accurate source as part of its payload when `UNCAUGHT_EXCEPTION` is dispatched. If omitted, a new instance of `SystemEvents` is created with source `core-http`. ```ts import { request } from '@procore/core-http' function ExampleRequest() { request('/some-url-here') .then((response) => console.log(response)) .catch((err) => console.error(err)) } ``` ### `requestJSON` _(deprecated)_ Fetches a resource, and attempts to cast the parsed JSON response as the specified type. It is **strongly** recommended to use `createClient` instead of this method, which supports both schema validation and error handling. ```ts requestJSON<T>(url: string, requestParams: RequestParams) ``` #### `url` - required The resource to fetch #### `requestParams` - optional _(default: {})_ Used to augment the resulting `fetch` call with additional configuration, such as `method` and `baseUrl`. ```ts import { requestJSON } from '@procore/core-http' function ExampleRequest() { requestJSON('/some-url-here.json') .then((response) => console.log(response)) .catch((err) => console.error(err)) } ``` ### Plugins Plugins provide an opportunity augment the behavior that occurs with each request. Examples of plugin behavior include: - Adding a header to every request. - Emit an event based on an 4xx or 5xx error response. - Display a modal based on an error response. - Instrument requests and responses with Open Telemetry or logging. The plugin interface is as follows: ```ts interface Plugin { onError?: (error, request) => void onRequest?: (request) => void onSuccess?: (data, request) => void } ``` `onRequest` functions are run when a request is initiated. `onSuccess` is called when a request successfully returns. Finally, `onError` is called when an error response is received. Refer to the ["Lifecycle Hooks"][lifecycle-hooks] section in the [up-fetch][lifecycle-hooks] docs for more details about each method. There are a few caveats to understand when working with lifecycle hooks: - Order matters! Lifecycle hooks execute in the following order: - Lifecycle hooks added to default options - Lifecycle hooks in each plugin, in order - Lifecycle hooks added to an individual request - Plugins only need to implement the lifecycle hooks that they need. For example, when implementing a plugin that only handle error responses, you can omit the `onRequest` and `onSuccess` methods. - The `defaultErrorHandling` option controls how errors are handled. By default, errors are still thrown even if you provide an `onError` handler; use `onError` for logging or other side effects rather than to suppress the error. ### Additional Considerations `createClient` opens up all of the functionality of [up-fetch][up-fetch], while providing some Procore-specific defaults. Features that you should consider using include: - [Simple query parameters][simple-query-parameters]: the library supports specifying query parameters via a simple `{params: {}}` property passed to your request. - [Automatic body handling][automatic-body-handling]: the library supports automatic serialization of the body on `POST` calls. - [Schema validation][schema-validation]: the library supports [Standard Schema][standard-schema]. This will not only validate the responses that you receive from a fetch call, but also return the data in the validate type (no more type casting!). [automatic-body-handling]: https://github.com/L-Blondy/up-fetch?tab=readme-ov-file#%EF%B8%8F-automatic-body-handling [client]: https://github.com/L-Blondy/up-fetch?tab=readme-ov-file#upfetchurl-options [lifecycle-hooks]: https://github.com/L-Blondy/up-fetch?tab=readme-ov-file#%EF%B8%8F-lifecycle-hooks [response-error]: https://github.com/L-Blondy/up-fetch?tab=readme-ov-file#-responseerror [schema-validation]: https://github.com/L-Blondy/up-fetch?tab=readme-ov-file#%EF%B8%8F-schema-validation [simple-query-parameters]: https://github.com/L-Blondy/up-fetch?tab=readme-ov-file#%EF%B8%8F-simple-query-parameters [standard-schema]: https://github.com/standard-schema/standard-schema [up-fetch]: https://github.com/L-Blondy/up-fetch [up]: https://github.com/L-Blondy/up-fetch?tab=readme-ov-file#upfetch-getdefaultoptions [validation-error]: https://github.com/L-Blondy/up-fetch?tab=readme-ov-file#-validationerror