xior

repository·main·Indexed 19 days ago

https://github.com/suhaotian/xior

A lightweight (~6KB) HTTP request library built on the native fetch API, offering a developer experience similar to Axios. It features strong TypeScript typing, support for interceptors, and a plugin system for functionality such as error retries, request throttling, deduplication, caching, and token refresh. xior is optimized for modern environments including Cloudflare Workers, Next.js, Vercel Edge Functions, and Expo.

Tokens
22.4K
Snippets
95
Records
103
Agent score
65%

What's inside xior

  1. Eject the Create React App configuration

    main

    If you need full control over the build tool and configuration (webpack, Babel, ESLint, etc.), you can run npm run eject.

    Warning: This is a one-way operation. Once you eject, you cannot go back.

    Ejecting copies all configuration files and transitive dependencies directly into your project. While all other commands will still work, they will point to the newly copied scripts, and you will be responsible for maintaining the configuration.

    npm run eject
  2. Get the original response using responseType: 'original'

    main

    By default, xior parses the response body (e.g., into JSON). If you need to access the raw response object (for example, to use a ReadableStream for line-by-line text processing), set the responseType option to 'original'. This returns an object in the format { response } where response is the original response object.

    import xior from 'xior';
    
    const http = xior.create();
    
    async function* makeTextFileLineIterator(fileURL) {
      const utf8Decoder = new TextDecoder('utf-8');
      // Use responseType: 'original' to get the raw response object
      const { response } = await http.get(fileURL, { responseType: 'original' });
      const reader = response.body.getReader();
      // ... rest of the stream processing logic
    }
  3. Configure passthrough behavior for unmatched requests

    main

    You can control what happens when a request does not match any registered mock handler using the onNoMatch option in the MockPlugin constructor.

    • onNoMatch: 'passthrough': All unmatched requests are automatically forwarded to the actual network.
    • .passThrough(): You can also call .passThrough() on a specific handler to force matched requests to go to the network instead of being intercepted.
    // Option 1: Global passthrough for all unmatched requests
    const mock = new MockPlugin(instance, { onNoMatch: 'passthrough' });
    
    // Option 2: Specific handler passthrough
    mock.onGet('/api/real-data').passThrough();
  4. Access response headers in Xior.js

    main

    Because Xior.js uses the Fetch API's Headers object for responses, you cannot access headers using bracket notation like a standard JavaScript object. You must use the .get() method on the headers property of the response object.

    // In Axios:
    const value = response.headers['x-header-name'];
    
    // In Xior.js:
    const value = response.headers.get('x-header-name');
  5. How xior plugins work

    main

    xior uses a plugin mechanism to extend its functionality. Plugins are registered using http.plugins.use().

    Crucial Execution Order: The plugin mechanism follows a 'first in, last run' pattern. If you register plugins in the order plugin1, plugin2, plugin3, they will execute in the order plugin3 $\rightarrow$ plugin2 $\rightarrow$ plugin1 during the request lifecycle.

    plugins.use(plugin1);
    plugins.use(plugin2);
    plugins.use(plugin3);
    
    // Run order: plugin3 → plugin2 → plugin1
  6. Handle data transformations via interceptors or callbacks

    main

    Since xior does not support transformRequest or transformResponse properties, use these alternatives:

    1. For Request Transformation: Use interceptors.request.use to modify the config object (e.g., changing headers or data) before the request is sent.
    2. For Response Transformation: Use a .then() callback to transform the response data after it is received.
    // Request transformation via interceptor
    xiorInstance.interceptors.request.use((config) => {
      if (config.url === '/endpoint') {
        delete config.headers['Content-Type'];
      }
      return config;
    });
    
    // Response transformation via .then()
    xiorInstance.get('/api').then((response) => {
      // transform response data here
      return response;
    });
  7. Migrate from fetch to xior for POST requests

    main

    For POST requests, xior allows you to pass the request body directly as the second argument. You can also pass a configuration object as the third argument to specify mode, cache, credentials, headers, redirect, and referrerPolicy, mirroring the native fetch options.

    import xior from 'xior';
    
    const http = xior.create({
      baseURL: 'http://example.com',
    });
    
    http
      .post(
        '/answer',
        { answer: 42 },
        {
          mode: 'cors', // no-cors, *cors, same-origin
          cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
          credentials: 'same-origin', // include, *same-origin, omit
          headers: {
            // 'Content-Type': 'application/json',
          },
          redirect: 'follow', // manual, *follow, error
          referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
        }
      )
      .then(({ data }) => {
        console.log(data);
      });
  8. Use Xior with Next.js and Vercel Edge Functions

    main
    This example demonstrates how to use xior within a Next.js environment, specifically ensuring compatibility with Vercel Edge Functions. The project provides implementations for both standard Node.js runtime APIs and the more restricted Vercel Edge runtime, which is useful for low-latency edge computing scenarios.
  9. Migrate from fetch to xior for GET requests

    main

    When migrating from the native fetch API to xior, you can replace manual URL string concatenation and .json() parsing with xior.create() and the params option. xior automatically extracts the data property from the response, which contains the parsed JSON body.

    import xior from 'xior';
    
    const http = xior.create({
      baseURL: 'http://example.com',
    });
    
    async function logMovies() {
      const { data: movies } = await http.get('/movies.json', {
        params: {
          page: 1,
          perPage: 10,
        },
      });
      console.log(movies);
    }
  10. Implement a custom data parser

    main

    By default, xior attempts to parse response text as JSON. To implement custom parsing logic (e.g., based on Content-Type), set responseType: 'custom' and use a response interceptor.

    import xior from 'xior';
    
    const http = xior.create({
      baseURL,
      responseType: 'custom', // Disables default parsing
    });
    
    const typeMatchers = [
      ['json', [/^application\/.*json$/, /^$/]],
      ['text', [/^text\//, /^image\/svg\+xml$/, /^application\/.*xml$/]],
    ] as const;
    
    http.interceptors.response.use(async (res) => {
      if (res.config.responseType !== 'custom') return res;
    
      const { response } = res;
      const headers = response?.headers;
      if (!response || headers.get('Content-Length') === '0') return res;
    
      const contentType = headers.get('Content-Type')?.split(';')?.[0]?.trim() || '';
    
      const matchedType = typeMatchers.find(([_, patterns]) =>
        patterns.some((pattern) => pattern.test(contentType))
      );
    
      if (matchedType) {
        const [method] = matchedType;
        res.data = await response[method]();
      }
    
      return res;
    });
    const http = xior.create({ responseType: 'custom' });
  11. Upload files with progress tracking

    main

    Use the FormData API for file uploads. To track upload progress, use the xior/plugins/progress plugin.

    import Xior from 'xior';
    import uploadDownloadProgressPlugin from 'xior/plugins/progress';
    
    const http = Xior.create({});
    
    // Register the progress plugin
    http.plugins.use(
      uploadDownloadProgressPlugin({
        progressDuration: 5 * 1000,
      })
    );
    
    const formData = new FormData();
    formData.append('file', fileObject);
    formData.append('field1', 'val1');
    
    http.post('/upload', formData, {
      onUploadProgress(e) {
        console.log(`Upload progress: ${e.progress}%`);
      },
    });
    import uploadDownloadProgressPlugin from 'xior/plugins/progress';
    // ... setup http instance
    http.plugins.use(uploadDownloadProgressPlugin());