0.2.1 • Published 2 years ago

@fluxninja-tools/graceful-js v0.2.1

Weekly downloads
-
License
ISC
Repository
-
Last release
2 years ago

Graceful-js (FluxNinja UI package)

Install using npm or yarn

#npm
npm i @fluxninja-tools/graceful-js
#yarn
yarn add @fluxninja-tools/graceful-js

Graceful-js is a powerful and intuitive React library designed to enhance your API communication by seamlessly handling retries based on status codes and intelligently understanding rate limiting headers and bodies. Its ability to automatically handle retries, allowing your application to gracefully recover from transient failures. Whether you're dealing with intermittent server issues or temporary network glitches, this library will make sure your API requests are retried in a controlled and efficient manner.

Config Graceful

Here is how you can configure graceful-js according to your needs. If you are using axios, make sure you pass the axios instance to the config. You can create an instance by using axios.create.

/**
 * Configuration object for the GracefulProvider component.
 * @property {AxiosInstance} axios - An Axios instance to use for making HTTP requests.
 * @property {string[]} urlList - A list of URLs to intercept and handle gracefully.
 * @property {GracefulTheme} theme - The theme object to use for styling the error components.
 * @property {Map<number, JSX.Element>} errorComponentMap - A map of HTTP status codes to custom error components to render for each code.
 * @property {JSX.Element} DefaultErrorComponent - The default error component to render if no custom component is provided for a given status code.
 * @property {JSX.Element} WaitingRoomErrorComponent - The error component to render for the waiting room.
 * @property {number} maxBackOffTime - maximum exponential back-off time in seconds. Default is 20 seconds.
 * @property {number} maxRequestResolveTime - maximum time in seconds to wait for a request to resolve. Default is 10 seconds.
 * @property {ExponentialBackOffFn} exponentialBackOffFn - Provide your own exponential back-off logic.
 */
export declare type Config = {
  axios?: AxiosInstance
  urlList?: string[]
  theme?: GracefulTheme
  errorComponentMap?: Map<number, JSX.Element>
  DefaultErrorComponent?: JSX.Element
  WaitingRoomErrorComponent?: JSX.Element
  maxBackOffTime?: number
  maxRequestResolveTime?: number
  /**
   *
   * @param status
   * @param numberOfRetries
   * @returns retryAfter - number time in seconds
   * This function is used if no retry after is specified by server. Graceful-js exponential back-off function is
   * inspired by this google example. For more information, see https://cloud.google.com/iot/docs/how-tos/exponential-backoff.
   * If you want to provide a custom exponential back-off function provide this function in config.
   * For inspiration, see https://github.com/fluxninja/graceful-js/blob/main/src/scenarios/decider.ts
   */
  exponentialBackOffFn?: ExponentialBackOffFn
}

Pass this config object to the GracefulProvider like so:

import { GracefulProvider } from 'graceful-js'

const App: FC = () => (
  <ThemeProvider>
    <GracefulProvider config={yourConfigObject}>
      <AppComponent />
    </GracefulProvider>
  </ThemeProvider>
)

To get support for the rate limit headers and body use gracefulRequest instead of a regular fetch or axios. This function will retry according to the provided parameters. In case there is no Retry-After provided by server, library do retries in case of 429, 503 and 504 using exponential back off. Here is how you use gracefulRequest with Axios and fetch.

import { gracefulRequest } from 'graceful-js'

// gracefulRequest with Axios
gracefulRequest <
  'Axios' >
  ('Axios',
  () => api.get('yourEndPoint'),
  (err, success) => {
    if (err) {
      // action on error
      return
    }
    // action on success
  })

// gracefulRequest with fetch
gracefulRequest <
  'Fetch' >
  ('Fetch',
  () => fetch('yourEndPoint'),
  (err, success) => {
    if (err) {
      // action on error
      return
    }
    // action on success
  })

Callback in gracefulRequest emit error or success response on every retry and it resolves with a promise once retries are done. This callback can be useful to show error to the user right away without waiting for the function to get resolved.

Hooks

You can also use useGracefulRequest hook. Here is the code snippet.

  const { isError, refetch, data, isLoading, isRetry, error } = useGracefulRequest<'Axios'>({
    typeOfRequest: 'Axios',
    requestFnc: () => api.get('api/rate-limit'),
  })

  // here are the hook types:

export declare type UseGracefulRequestProps<T extends 'Axios' | 'Fetch'> = {
    typeOfRequest: T;
    requestFnc: () => Promise<AxiosOrFetch<T>>;
    options?: {
      // if disabled, api call will happen when called refetch
        disabled?: boolean;
    };
};
export declare type UseGracefulRequestReturn<
  T extends 'Axios' | 'Fetch',
  TData = any
> = {
  isError: boolean
  isLoading: boolean
  isRetry: boolean
  data: TData | null
  error: AxiosOrFetchError<T, TData> | null
  errorComponent: JSX.Element | null
  refetch: () => void
}

You can also use useGraceful hook to get last request response context along with a Map of errors happened in the app.

Error Components

To use error components user can use use the following:

<GracefulError
  {...{
    url: 'http://localhost:3009/api/ping', // endpoint for which this error component is rendering
    method: 'GET', // method for the request
    requestBody: {}, // request body in request, omit it if no request body
  }}
/>
// or
<GracefulErrorByStatus status={errorStatus} />
const { errorInfo } = useGraceful()

errorInfo.get('http://localhost:3009/api/rate-limit-get-{}') // create key with url + lowercase method + requestBody (if no request body add {})

Scenarios

To get support for the rate limiting scenario by using the response body, send the following inside the error response body.

export declare type RateLimitResponseBody = {
  message: string // message in the response
  retryAfter: number // time in seconds after which retry will happen
  retryLimit: number // max number of retries if not resolved
  global: boolean // if true, full app is rate limited. ie. app is down
  rateLimitRemaining: number // Remaining rate limit
  rateLimitReset: number // delta seconds after which rate-limit will reset
}

To get support for rate limiting scenario by using headers. Add following headers:

/**
 * Generic rate limit headers
 */
export declare type RateLimitHeaders = {
  /**
   * The number of requests that can be made
   */
  'x-ratelimit-limit': string
  /**
   * The number of remaining requests that can be made
   */
  'x-ratelimit-remaining': string
  /**
   * The number of seconds(delta-seconds) until the rate limit resets
   */
  'x-ratelimit-reset': string
  /**
   * Returned only on HTTP 429 responses if the rate limit encountered is the global rate limit (not per-route)
   */
  'x-ratelimit-global': string
  /**
   * The scope of the rate limit upto which it is valid. For example,
   * "user" or "bot"
   */
  'x-ratelimit-scope': string
  /**
   * The number of seconds after which the rate limit resets
   */
  'retry-after': string
}

NOTE: Retry will occur regardless of the error status code if a retry-after time is provided in headers or body.

0.2.1

2 years ago

0.2.0

2 years ago

0.1.5

2 years ago

0.1.4

2 years ago

0.1.3

2 years ago

0.1.2

2 years ago

0.1.1

2 years ago

0.1.0

2 years ago

1.0.0

2 years ago