mande

repository·main·Indexed 23 days ago

https://github.com/posva/mande

A lightweight, extensible wrapper around the native fetch API (version 2.0.10) providing smart defaults for JSON handling, header management, and authentication. It supports TypeScript generics for response typing, request interceptors via withInterceptors(), and specialized configurations for SSR and Nuxt 2 environments using nuxtWrap.

Tokens
4.2K
Snippets
14
Records
26
Agent score
80%

What's inside mande

  1. Configure mande for SSR (Server-Side Rendering)

    main

    When running on a server (e.g., Node.js), mande requires a fetch polyfill and absolute URLs (including the domain), as relative paths like /api/... will not resolve.

    // Example setup for SSR
    export const BASE_URL = process.server
      ? (process.env.NODE_ENV !== 'production' ? 'http://localhost:3000' : 'https://example.com')
      : '' // Client uses relative paths
    
    const fetchPolyfill = process.server ? require('node-fetch') : fetch
    const contents = mande(BASE_URL + '/api', {}, fetchPolyfill)
    export const BASE_URL = process.server
      ? process.env.NODE_ENV !== 'production'
        ? 'http://localhost:3000'
        : 'https://example.com'
      : // on client, do not add the domain, so urls end up like `/api/something`
        ''
    
    const fetchPolyfill = process.server ? require('node-fetch') : fetch
    const contents = mande(BASE_URL + '/api', {}, fetchPolyfill)
  2. Override or delete headers in mande requests

    main

    To remove a header that was set at the instance level or globally, pass null as the value for that header key in the request options. To override a header for a single request, provide the new value in the options object.

    const legacy = mande('/api/v1/data', {
      headers: {
        'Content-Type': 'application/xml',
      },
    })
    
    // This request overrides the 'Accept' and 'Content-Type' headers specifically for this call
    legacy.post(new FormData(), {
      headers: {
        Accept: null,
        'Content-Type': null,
      },
    })
    const legacy = mande('/api/v1/data', {
      headers: {
        // override all requests
        'Content-Type': 'application/xml',
      },
    })
    
    // override only this request
    legacy.post(new FormData(), {
      headers: {
        // overrides Accept: 'application/json' only for this request
        Accept: null,
        'Content-Type': null,
      },
    })
  3. Configure mande for Nuxt 2 SSR

    main

    When using mande with Nuxt 2 in SSR mode, you must use nuxtWrap to ensure that requests on the server automatically proxy cookies and headers. Additionally, you must add mande/nuxt to your buildModules in nuxt.config.js to prevent accidental header/token sharing between requests.

    1. Wrap your API functions:

    import { mande, nuxtWrap } from 'mande'
    const fetchPolyfill = process.server ? require('node-fetch') : fetch
    const users = mande(BASE_URL + '/api/users', {}, fetchPolyfill)
    
    export const getUserById = nuxtWrap(users, (api, id: string) => api.get(id))

    2. Update nuxt.config.js:

    module.exports = {
      buildModules: ['mande/nuxt'],
    }

    3. Update tsconfig.json for TypeScript support:

    {
      "types": ["@types/node", "@nuxt/types", "mande/nuxt"]
    }
  4. Basic usage of mande

    main

    mande is a wrapper around fetch that provides smart defaults for API communication. Instead of manually handling JSON stringification, headers, and response status checks, you can create a mande instance for a specific base URL and use HTTP verb methods like .get(), .post(), etc.

    import { mande } from 'mande'
    
    // Create an instance for a specific API resource
    const users = mande('/api/users')
    
    // Perform a POST request with a JSON body
    users
      .post({
        name: 'Dio',
        password: 'irejectmyhumanityjojo',
      })
      .then((user) => {
        // 'user' is the parsed JSON response
      })
    import { mande } from 'mande'
    
    const users = mande('/api/users')
    
    users
      .post({
        name: 'Dio',
        password: 'irejectmyhumanityjojo',
      })
      .then((user) => {
        // ...
      })
  5. Manage Authorization tokens in mande instances

    main

    You can manage authentication by updating the options.headers property on a mande instance. These changes will apply to all subsequent requests made by that instance.

    import { mande } from 'mande'
    
    const todos = mande('/api/todos')
    
    // Set the token for all future requests from this instance
    export function setToken(token) {
      todos.options.headers.Authorization = 'Bearer ' + token
    }
    
    // Remove the token
    export function clearToken() {
      delete todos.options.headers.Authorization
    }
    import { mande } from 'mande'
    
    const todos = mande('/api/todos', todosApiOptions)
    
    export function setToken(token) {
      // todos.options will be used for all requests
      todos.options.headers.Authorization = 'Bearer ' + token
    }
    
    export function clearToken() {
      delete todos.options.headers.Authorization
    }
    
    export function createTodo(todoData) {
      return todo.post(todoData)
    }
  6. Local development setup for My Module

    main

    If you are contributing to my-module, use the following commands to set up your local environment, run the playground, and execute tests.

    # Install dependencies
    npm install
    
    # Generate type stubs
    npm run dev:prepare
    
    # Develop with the playground
    npm run dev
    
    # Build the playground
    npm run dev:build
    
    # Run ESLint
    npm run lint
    
    # Run Vitest
    npm run test
    npm run test:watch
    
    # Release new version
    npm run release
  7. Create a Mande instance

    main

    Initialize a new mande instance by providing a baseURL. You can also pass passedInstanceOptions that will be applied to every request made by this instance, and an optional localFetch (useful for SSR environments like Node.js).

    const users = mande('/api/users')
    users.get('2').then(user => {
      // do something
    })
  8. Use nuxtWrap for Nuxt 2 SSR compatibility

    main
    To ensure cookies are correctly proxied and requests work transparently on both the server and client in Nuxt 2, use the nuxtWrap function. This allows you to augment the Mande instance on the server side before executing the API call.
  9. Configure ESLint for Nuxt modules using createConfigForNuxt

    main

    To set up ESLint for a Nuxt module project, use createConfigForNuxt from @nuxt/eslint-config/flat. This utility provides pre-configured rules tailored for Nuxt development.

    You can enable specific rule sets via the features option:

    • tooling: Enables rules specifically designed for module authors.
    • stylistic: Enables rules for code formatting.

    The dirs option allows you to specify which directories should be included in the linting process (e.g., src: ['./playground']).

    You can extend the generated configuration by calling .append() with your own custom flat configuration objects.

    import { createConfigForNuxt } from '@nuxt/eslint-config/flat'
    
    export default createConfigForNuxt({
      features: {
        // Rules for module authors
        tooling: true,
        // Rules for formatting
        stylistic: true,
      },
      dirs: {
        src: ['./playground'],
      },
    }).append(
      // your custom flat config here...
    )
  10. Handling FormData with mande

    main

    When passing FormData as a body, mande automatically removes the Content-Type header so the browser can set the correct boundary. If you need to manually set it, you can do so in the instance or the specific request.

    // Option 1: Remove Content-Type globally for the instance
    const api = mande('/api/', { headers: { 'Content-Type': null } })
    
    // Option 2: Set it specifically for one request
    const formData = new FormData()
    api.post(formData, {
      headers: { 'Content-Type': 'multipart/form-data' },
    })
    // directly pass it to the mande instance
    const api = mande('/api/', { headers: { 'Content-Type': null } })
    // or when creating the request
    const formData = new FormData()
    api.post(formData, {
      headers: { 'Content-Type': 'multipart/form-data' },
    })