@bubblesjs/request

@bubblesjs/request is a small Alova-based request wrapper. It centralizes common headers, response transformation, business-code checks, message hooks, cache adapters, and per-request behavior switches.

Installation

pnpm
npm
yarn
bun
pnpm add @bubblesjs/request alova

Quick Start

import { createInstance } from '@bubblesjs/request'

const request = createInstance({
  baseUrl: '/api',
  commonHeaders: {
    'Content-Type': 'application/json',
    Authorization: async () => `Bearer ${await getToken()}`
  },
  successMessageFunc: (message) => toast.success(message),
  errorMessageFunc: (message) => toast.error(message),
  unAuthorizedResponseFunc: () => router.push('/login')
})

const user = await request.Get('/user/info')
const result = await request.Post('/user/update', {
  body: { name: 'John' }
})

By default, a successful wrapped response is expected to look like this:

{
  code: 200,
  data: { id: 1 },
  message: 'ok'
}

The returned value is the field configured by responseDataKey, so the example above resolves to { id: 1 }.

Dual Call Instance

createDualCallInstance returns a default Alova instance that can also be called as a function to create a temporary instance with merged options.

import { createDualCallInstance } from '@bubblesjs/request'

const request = createDualCallInstance({
  baseUrl: '/api',
  isShowSuccessMessage: false,
  errorMessageFunc: (message) => toast.error(message)
})

await request.Get('/user/profile')

await request({
  isShowSuccessMessage: true,
  successMessageFunc: (message) => toast.success(message)
}).Post('/user/create', {
  body: { name: 'Jane' }
})

Configuration

OptionTypeDefaultDescription
baseUrlstring'/'Base URL passed to Alova as baseURL.
timeoutnumberundefinedRequest timeout.
commonHeadersRecord<string, HeaderValue>{}Common headers. Values can be static or async functions.
statusMapStatusMap{ success: 200, unAuthorized: 401 }HTTP status matchers.
codeMapCodeMap{ success: [200], unAuthorized: [401] }Business-code matchers.
responseCodeKeystring'code'Business-code field in wrapped responses.
responseDataKeystring'data'Data field in wrapped responses.
responseMessageKeystring'message'Message field in wrapped responses.
isWrappedbooleantrueWhether the response uses { code, data, message } style wrapping.
isTransformResponsebooleantrueWhether to transform responses in responded.onSuccess.
isShowSuccessMessagebooleanfalseWhether to call successMessageFunc after business success.
successDefaultMessagestring'操作成功'Fallback success message.
isShowErrorMessagebooleantrueWhether to call errorMessageFunc on HTTP, business, or network errors.
errorDefaultMessagestring'服务异常'Fallback error message.
successMessageFunc(message: string) => voidundefinedSuccess message hook.
errorMessageFunc(message: string) => voidundefinedError message hook.
unAuthorizedResponseFunc() => voidundefinedCalled for unauthorized HTTP status or business code.
statesHookStatesHookundefinedAlova state hook for React, Vue, and other integrations.
requestAdapterAlovaRequestAdapteradapterFetch()Alova request adapter.
cacheForGlobalCacheConfig | nullnullAlova global cache config.
cacheLoggerbooleantrueAlova cache logging switch.
l1CacheAlovaGlobalCacheAdapterundefinedAlova L1 cache adapter.
l2CacheAlovaGlobalCacheAdapterundefinedAlova L2 cache adapter.
storageAdapterAlovaGlobalCacheAdapterundefinedBackward-compatible alias for l2Cache.

Matching Rules

statusMap.success and statusMap.unAuthorized accept a number, an array of numbers, or a function:

const request = createInstance({
  statusMap: {
    success: [200, 201, 204],
    unAuthorized: (status) => status === 401 || status === 419
  }
})

codeMap.success and codeMap.unAuthorized accept arrays of numbers or strings:

const request = createInstance({
  responseCodeKey: 'status',
  responseMessageKey: 'msg',
  codeMap: {
    success: [0, 'OK'],
    unAuthorized: [401, 'TOKEN_EXPIRED']
  }
})

Per-Request Meta

The following options can be overridden on a single request through meta:

  • isWrapped
  • isTransformResponse
  • isShowSuccessMessage
  • isShowErrorMessage
const rawResponse = await request.Get('/download/file', {
  meta: {
    isTransformResponse: false,
    isShowErrorMessage: false
  }
})

const rawUploadData = await request.Post('/upload', {
  body: formData,
  meta: {
    isWrapped: false
  }
})

Response Shapes

The default fetch adapter returns a Response, which is parsed according to the content-type header. JSON responses are read with json(), text responses are read with text(), and JSON strings are parsed when possible.

Axios-style and Taro-style responses are also supported:

// Axios-style
{
  status: 200,
  data: { code: 200, data: [], message: 'loaded' }
}

// Taro-style
{
  statusCode: 200,
  data: { code: 200, data: { ok: true }, msg: 'done' }
}

For Taro upload APIs that return data as a JSON string, the string is parsed before codeMap and responseDataKey are applied.

Custom Adapter

Any Alova request adapter can be passed through requestAdapter. If the adapter also exposes a cache adapter, pass it as l2Cache or storageAdapter.

import { createInstance } from '@bubblesjs/request'
import { adapterTaro } from '@alova/adapter-taro'

const taroAdapter = adapterTaro()

const request = createInstance({
  baseUrl: '/api',
  requestAdapter: taroAdapter.requestAdapter,
  storageAdapter: taroAdapter.storageAdapter,
  responseMessageKey: 'msg'
})

Types

type MaybePromise<T> = T | Promise<T>

type HeaderValue =
  | string
  | number
  | boolean
  | null
  | undefined
  | (() => MaybePromise<string | number | boolean | null | undefined>)

type StatusMatcher<RE> =
  | number
  | number[]
  | ((status: number, response: RE) => boolean)

type CodeMatcher = Array<number | string>

interface StatusMap<RE = unknown> {
  success?: StatusMatcher<RE>
  unAuthorized?: StatusMatcher<RE>
}

interface CodeMap {
  success?: CodeMatcher
  unAuthorized?: CodeMatcher
}

interface RequestMeta {
  isWrapped?: boolean
  isTransformResponse?: boolean
  isShowSuccessMessage?: boolean
  isShowErrorMessage?: boolean
}