ClientPlugins

Plugins

feTS Client provides a powerful plugin system that allows you to hook into the request/response lifecycle and customize the behavior of the client.

feTS Client Plugin

A plugin in feTS is a function that returns an object with the following optional properties:

  • onRequestInit - Called before the request is sent by the client.
    • It includes an endResponse method that accepts a Response object to short-circuit the request.
  • onFetch - Called when the underlying fetch function is invoked by the client.
    • It allows you to modify the fetch options or replace the fetch function itself.
  • onResponse - Called after the request is handled by the router.
    • It enables you to modify the response or throw an error to prevent the response from being returned.

Handling Cookies With Built-in Cookies Plugin

To handle cookies separately from the environment, you can use the useCookieStore plugin.

Note: This plugin is not needed for browsers.

import { CookieStore, useClientCookieStore } from 'fets'
 
// Create a cookie store with a cookie string
const cookieStore = new CookieStore('')
 
const client = createClient<typeof someoas>({
  plugins: [useClientCookieStore(cookieStore)]
})

You can store cookies in a custom storage by utilizing the events provided by feTS’ CookieStore object, which implements the WHATWG CookieStore. Learn more in the official documentation

Retrying on Fail With ‘Fetch-Retry’ as a Plugin

You can install fetch-retry from npm and use it to create a plugin:

import fetchRetry from 'fetch-retry'
 
export function useRetry(): ClientPlugin {
  return {
    onFetch({ fetchFn, setFetchFn }) {
      setFetchFn(fetchRetry(fetchFn))
    }
  }
}
 
const client = createClient<typeof someoas>({
  plugins: [useRetry()]
})

HTTP Caching on Non-Browser Environments With ‘fetchache’

Server environments typically lack the built-in HTTP caching logic found in browsers. To bring this behavior to server-side environments, you can use fetchache.

fetchache requires a key-value cache implementation, which can be Redis or a simple in-memory cache. The following example demonstrates using an in-memory approach:

import { fetchFactory, KeyValueCache } from 'fetchache'
import { ClientPlugin } from 'fets'
 
const mapForCache = new Map<string, any>()
 
const cache: KeyValueCache = {
  async get(key) {
    return _map.get(key)
  },
  async set(key, value) {
    _map.set(key, value)
  },
  async delete(key) {
    _map.delete(key)
  }
}
 
const useCache: ClientPlugin = {
  onFetch({ fetchFn, setFetchFn }) {
    setFetchFn(
      fetchFactory({
        fetch: fetchFn,
        cache,
        Response
      })
    )
  }
}
 
const client = createClient<typeof someoas>({
  plugins: [useCache()]
})

Adding Auth Headers to Requests

import { ClientPlugin } from 'fets'
 
export function useAuth(token: string): ClientPlugin {
  return {
    onRequestInit({ requestInit }) {
      requestInit.headers = {
        ...requestInit.headers,
        Authorization: `Bearer ${token}`
      }
    }
  }
}
 
const client = createClient<typeof someoas>({
  plugins: [useAuth()]
})

Calculating a request’s duration

import { ClientPlugin } from 'fets'
 
export function useDuration(): ClientPlugin {
  const startTimeByRequestInit = new WeakMap<RequestInit, number>()
  return {
    onRequestInit({ requestInit }) {
      startTimeByRequestInit.set(requestInit, Date.now())
    },
    onResponse({ response }) {
      const start = startTimeByRequestInit.get(response.requestInit)
      if (start) {
        const duration = Date.now() - Number(start)
        console.log(`Request took ${duration}ms`)
      }
    }
  }
}