0.6.3 • Published 2 months ago

nuxt-strict-fetch v0.6.3

Weekly downloads
-
License
MIT
Repository
github
Last release
2 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.6.3

2 months ago

0.6.2

4 months ago

0.6.1

4 months ago

0.5.1

5 months ago

0.4.5

5 months ago

0.4.4

5 months ago

0.4.6

5 months ago

0.2.1

5 months ago

0.4.1

5 months ago

0.3.2

5 months ago

0.3.1

5 months ago

0.2.2

5 months ago

0.4.3

5 months ago

0.4.2

5 months ago

0.1.10

5 months ago

0.1.11

5 months ago

0.1.12

5 months ago

0.1.8

5 months ago

0.1.7

5 months ago

0.1.6

5 months ago

0.1.5

5 months ago

0.1.4

5 months ago

0.1.3

5 months ago

0.1.1

5 months ago