0.7.2 • Published 4 months ago

nuxt-strict-fetch v0.7.2

Weekly downloads
-
License
MIT
Repository
github
Last release
4 months ago

Nuxt Strict Fetch

npm version npm downloads License Nuxt

My new Nuxt module for doing amazing things.

Features

  •  Foo
  • 🚠  Bar
  • 🌲  Baz

Quick Setup

  1. Add nuxt-strict-fetch dependency to your project
# Using pnpm
pnpm add -D nuxt-strict-fetch

# Using yarn
yarn add --dev nuxt-strict-fetch

# Using npm
npm install --save-dev nuxt-strict-fetch
  1. Add nuxt-strict-fetch to the modules section of nuxt.config.ts
export default defineNuxtConfig({
  modules: [
    'nuxt-strict-fetch'
  ]
})

That's it! You can now use Nuxt Strict Fetch in your Nuxt app ✨

Module Options

export default defineNuxtConfig({
  modules: [
    'nuxt-strict-fetch'
  ],
  strictFetch: {
    // base URL for all requests
    baseURL: '/api/',
  },
})

Plugin Setup

If you wish to define some global options for all requests in plugin, you can use the StrictFetch.init:

export default defineNuxtPlugin(() => {
  const { $csrfFetch } = useNuxtApp(); // nuxt-csurf module $csrfFetch for example

  StrictFetch.init({
    onRequest(context) {
      context.options.headers = {
        ...context.options.headers,
        Custom: 'Value',
      };
    },
    fetch: $csrfFetch, // option for default Nuxt $fetch reassignment
  });
});

Define Methods

Nuxt Strict Fetch module assumes the following method declaration:

// ~/api/common.ts for example

type Filter = { name?: string };

type Item = { id: number; name: string; };

type List = { items: Item[] };

type CreateItem = { name: string; };

const CommonAPI = {
  list: StrictFetch.prepare<List, null, null, Filter>({
    url: 'list',
  }),
  create: StrictFetch.prepare<Item, CreateItem>({
    url: 'list',
    method: HTTPMethod.post,
  }),
  details: StrictFetch.prepare<Item, null, { id: number }>({
    url: ({ id }) => `list/${id}`,
  }),
}

prepare method has the following generic types:

StrictFetch.prepare<R /* response body */, B /* common body */, P /* common params */, Q /* common query */>

Also, you may define API with validation schemas:

// ~/api/common.ts for example
import * as yup from 'yup';

const filterSchema = yup.object().shape({ name: yup.string() });

const itemSchema = yup.object().required().shape({
  id: yup.string().required(),
  name: yup.string().required(),
});

const listSchema = yup.array().required().of(itemSchema);

const createItemBodySchema = yup.object().required().shape({
  name: yup.string().required(),
});

const detailsParamsSchema = yup.object().required().shape({
  id: yup.number().required(),
});

const CommonAPI = {
  list: StrictFetch.prepare({
    url: 'list',
    schemas: { response: listSchema, query: filterSchema },
  }),
  create: StrictFetch.prepare({
    url: 'list',
    method: HTTPMethod.post,
    schemas: { response: itemSchema, body: createItemBodySchema },
  }),
  details: StrictFetch.prepare({
    url: ({ id }) => `list/${id}`,
    schemas: { response: itemSchema, params: detailsParamsSchema },
  }),
}

The second way also provide useRequest composable validation feature.

Composables

Module provides the following composable methods:

  • useRequest - method for using API method validation, state and TypeScript supporting for data.
const name = ref('');

const {
  execute, // execute common after validation (execution will return undefined if validation failed or loading is processed)
  parameters, // reactive data provided to second useRequest argument
  isValid, // reactive variable for validation result
  isLoading, // reactive variable for loading state
} = useRequest(
  CommonAPI.create,
  () => ({
    body: { name: name.value },
  }),
  {
    validation: () => false, // additional validation that will be included in isValid variable
  },
);

const onSubmit = () => {
  execute()?.then( /* ... */ ).catch( /* ... */ );
};

Subscriptions

Nuxt Strict Fetch module provides the opportunity to subscribe to API events:

StrictFetch.hooks.subscribe('method:create:start', () => { /* ... */ });

StrictFetch.hooks.subscribe('method:create:finish', () => { /* ... */ });

StrictFetch.hooks.unsubscribe('method:create:start', () => { /* ... */ });

StrictFetch.hooks.unsubscribe('method:create:finish', () => { /* ... */ });

// where `create` is methodKey field of the method

Order Requests

You can to create order of requests. When orderKey: 'my-order' key is added to methods, its will be executed only one by one. In other words, when second request with orderKey: 'my-order' will be executed, request will be sent only after first finish.

const OrderAPI = {
  first: StrictFetch.prepare({
    url: 'first',
    orderKey: 'my-order',
  }),
  second: StrictFetch.prepare({
    url: 'second',
    orderKey: 'my-order',
  }),
};

OrderAPI.first();
OrderAPI.second(); // will wait for first common finish

Other helpful options

const options = {
  selfInterrupted: true, // will interrupt previous requests when new common will be executed
  onError: () => { /* ... */ }, // will be called on error but ignored 'AbortError' error
  methodKey: 'my-method', // key of method for subscribing
  orderKey: 'my-order', // key of order for subscribing or ordering requests
  proxyServerCookies: true, // will send cookies from browser for common on server side
};

/** global options injected in StrictFetch */
StrictFetch.init(options);

// or

/** options for current common */
StrictFetch.prepare({
  url: 'url',
  ...options,
})

Work with FormData

Nuxt Strict Fetch have three ways to work with FormData:

const FormDataAPI = {
  first: StrictFetch.prepare<null, FormData>({
    url: 'form-data-url',
    method: HTTPMethod.post,
  }),
  second: StrictFetch.prepare<null, { name: string; file: File; tags: string[] }>({
    url: 'form-data-url',
    method: HTTPMethod.post,
    // will be converted to FormData automatically
    // `name` and `file` as is
    // `tags` as array of FormData fields with the same key
    formData: true,
  })
};

// in plugin
// global declaration to use FormData common body
StrictFetch.init({ formData: true });

// in methods file
const CommonAPI = {
  formDataMethod: StrictFetch.prepare<null, { name: string; file: File; tags: string[] }>({
    url: 'form-data-url',
    method: HTTPMethod.post,
  }),
  jsonDataMethod: StrictFetch.prepare<null, { name: string; tags: string[] }>({
    url: 'json-data-url',
    method: HTTPMethod.post,
    formData: false, // disabling for current method
  }),
};

Global catching

If you wish to define some logic for catching request, you might do it in options:

StrictFetch.init({
  onRequest(context) {
    const expiredAt = localStorage.getItem('token')
    if (expiredAt) context.options.headers = { 'x-token': expiredAt }
  },
  catch(error) {
    if (error.context.response.status === 401) {
      const expiredAt = new Date()
      expiredAt.setSeconds(expiredAt.getSeconds() + 5)
      localStorage.setItem('token', expiredAt.toISOString())

      // Request repeating for example
      return $fetch(error.context.request, error.context.options);
    }

    throw error
  },
})

Import types

You can import types from nuxt-strict-fetch/types path.

import type { StrictFetchOptions } from 'nuxt-strict-fetch/types';

Development

# 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
0.7.2

4 months ago

0.7.1

4 months ago

0.6.3

8 months ago

0.6.2

10 months ago

0.6.1

11 months ago

0.5.1

11 months ago

0.4.5

12 months ago

0.4.4

12 months ago

0.4.6

12 months ago

0.2.1

12 months ago

0.4.1

12 months ago

0.3.2

12 months ago

0.3.1

12 months ago

0.2.2

12 months ago

0.4.3

12 months ago

0.4.2

12 months ago

0.1.10

12 months ago

0.1.11

12 months ago

0.1.12

12 months ago

0.1.8

12 months ago

0.1.7

12 months ago

0.1.6

12 months ago

0.1.5

12 months ago

0.1.4

12 months ago

0.1.3

12 months ago

0.1.1

12 months ago