npm.io
14.0.0 • Published 2 weeks ago

@procore/core-http

Licence
SEE LICENSE IN LICENSE
Version
14.0.0
Deps
2
Size
95 kB
Vulns
0
Weekly
0

@procore/core-http

A simple HTTP client. Built on top of up-fetch!

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

Simple Example
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)
    })
}
Customize options, such as baseUrl
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' })

API

createClient({ errorReportingApiKey, defaults?, plugins?, fetchFn?, defaultErrorHandling?, systemEvents? })
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 function passed to the up function in up-fetch. Refer to the getDefaultOptions 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" below for more details.

fetchFn - optional (default: fetch)

The function that is used to actually make the fetch calls. Equivalent to the fetch parameter in the up function in 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.

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.

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 docs.

isValidationError(error: unknown): boolean

Returns true when the error is a schema validation error. Exported from up-fetch.

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.

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.
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.

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.

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:

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" section in the up-fetch 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, while providing some Procore-specific defaults. Features that you should consider using include:

  • Simple query parameters: the library supports specifying query parameters via a simple {params: {}} property passed to your request.
  • Automatic body handling: the library supports automatic serialization of the body on POST calls.
  • Schema validation: the library supports 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!).

Keywords