UNPKG

xfetch-hook

Version:
197 lines (155 loc) 6.87 kB
# Fetch/XHR Middleware **Alpha release - API may change** Extremly simple way to: - Intercept a fetch/XHR request - Listen on a fetch/XHR request ## Usage: Can be used with or without ES modules. Use only `fetch` or `XMLHttpRequest` or both at the same time. Check all the available [CDN exports](https://unpkg.com/browse/xfetch-hook@latest/dist/): Or install it via npm: ``` npm install xfetch-hook@latest ``` ```javascript import { fetchHook, xhrHook } from 'xfetch-hook' ``` **ES modules via CDN** ```html <script type="module"> // Import both, fetch and xhr import * as xfetch from 'https://unpkg.com/browse/xfetch-hook@latest/dist/fetch-xhr.module.min.js' const { fetchHook, xhrHook } = xfetch // Or only import what's needed import fetchHook from 'https://unpkg.com/browse/xfetch-hook@latest/dist/fetch.module.min.js' import xhrHook from 'https://unpkg.com/browse/xfetch-hook@latest/dist/xhr.module.min.js' // Initialize once fetchHook() xhrHook() // Start using by registering your middleware functions! (See the middleware functions signature below) fetch.onRequest(fetchMiddleware1) fetch.onRequest(fetchMiddleware2) XMLHttpRequest.onRequest(xhrMiddleware1) XMLHttpRequest.onRequest(xhrMiddleware2) // ... register any number of hooks </script> ``` **NON-ESM/Global export via CDN** ```html <!-- Load the script --> <script src="https://unpkg.com/browse/xfetch-hook@latest/dist/fetch-xhr.module.min.js"> </script> <!-- `xfetch` is now accessible --> <script> // Initialize once xfetch.fetchHook() xfetch.xhrHook() // Start using by registering your middleware functions! fetch.onRequest(middleware1) ... </script> ``` ### Fetch Middleware Once the hook is initialized, `onRequest` function will be exposed on the interface. It expects a middleware function which is called before any fetch request is made, giving you a chance to listen or intercept it. ```js async function middleware({ request, url, headers }) { // Return null/falsy if this request does not need to be hooked return null return { // Optional - if specified, original request will be overriden - Must be an instance of `Request` class request?: new Request(), // Optional - if specified, actual network call will not be made (bypass mechanism) - Must be an instance of `Response` class response?: new Response(), // Optional - if specified, `listen` function will receive `parsedData` as second argument // Required - if `transformResponse`, is specified as?: 'json' | 'text' | 'blob' | 'arrayBuffer' | 'formData' // Optional - Provide a listener function. It will be called with `response`, once the request is complete // `parsedData` will be available if `as` is specified. listen?: (response, parsedData?) => {}, // Once the network call is made, the parsed response data will be transformed using this transformer function // and then passed to the original caller. Think of it as a hook to transform any data before it reaches to the caller. // `listen` function will always receive the transformed response and transformed data transformResponse?: (parsedData) => { return modifyData(parsedData) } } } ``` **XMLHttpRequest Middleware** ```js async function middleware({ method, url, body, headers }) { // Overrite any of the paramer return { // Optional - override the method of xhr. (GET, POST, PUT, etc) method?: 'String', // Optional - override the xhr URL. Can be string or instance of `new URL()` class url?: 'String' | new URL() // Optional - update the body of the request body, // Optional - update the headers of the request - Must be an instance of `new Headers()` class headers, // Optional - Provide a listener function. It will be called with `response` and `xhr`, once the request is complete // Note: `data` refers to `xhr.response`, (not to be confused with `new Response()` constructor) listen?: (data, xhr) => { }, // Once the network call is made, the response data will be transformed using this transformer function, // and then passed to the original caller. Think of it as a hook to transform any data before it reaches to the caller. // `listen` function will always receive the transformed data transformResponse?: (data) => { return modifyData(data) } } } ``` Hooks/middlewares are called in the order they're registered. If any middleware modifies the data (`request`, `response`, `as`, `method`, `url`, `body`, `transformResponse`), the next middleware will receive the modified data ### Working example Shopify provides the following endpoings for cart: - GET `/cart.js` or `/cart.json` - POST `/cart/update.js` and `/cart/change.js` These endpoints return cart data in JSON format. In this example, we want to hide particular items in the cart. ```html <script type="module"> import fetchHook from 'https://unpkg.com/browse/xfetch-hook@latest/dist/fetch.module.min.js' // Initialize once fetchHook() // Hook for GET: '/cart.json' const unsubscribe1 = fetch.onRequest(async ({ url, request }) => { const isCartRequest = /\/cart.js(on)?/.test(url.pathname) // If it's not `/cart.json` request, don't do anything if (!isCartRequest) return // we need to listen on only GET requests if (request.method !== 'GET') return return { // `cartJson` has `items` array. We will filter out all the items that have 'HIDDEN' product_type // so that original caller will receive the modified data transformResponse: cartJson => { cartJson.items = cartJson.items.filter(item => item.product_type === 'HIDDEN') return cartJson }, } }) // Hook for /cart/change.js and /cart/update.js const unsubscribe2 = fetch.onRequest(async ({ url, request }) => { const isCartChangeOrUpdateRequest = /\/cart\/(change|update).js(on)?/.test(url.pathname) if (!isCartChangeOrUpdateRequest) return return { // `cartJson` has `items` array. We will filter out all the items that have 'HIDDEN' product_type // so that original caller will receive the modified data transformResponse: cartJson => { cartJson.items = cartJson.items.filter(item => item.product_type === 'HIDDEN') return cartJson }, // Since this is a POST request, it means the cart has been updated // We can listen for the latest data, and react to it as: 'json', listen: (response, cartJson) => { // `cartJson` is the trasnfromed cart object console.log('Cart has been updated. Time to update our UI', cartJson) } } }) </script> ``` Each `fetch.onRequest` middleware registration returns an `unsubscribe` function. Hint hint, this `unsubscribe` function's job is to remove the registered middleware.