npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

requete

v1.1.8

Published

A lightweight client-side HTTP request library based on the Fetch API and supports middleware.

Downloads

34

Readme

requete

requete is the French word for request

npm version install size npm bundle size build status codecov

requete is a lightweight client-side HTTP request library based on the Fetch API, and supports middleware for processing requests and responses. It provides APIs similar to Axios.

In addition, requete also includes an XMLHttpRequest adapter, which allows it to be used in older browsers that do not support Fetch, and provides polyfills to simplify import.

Also, requete supports usage in Node.js, using fetch API (nodejs >= 17.5.0).

Features

  • Use Fetch API on modern browsers or Node.js
  • Use XMLHttpRequest on older browsers
  • Supports middleware for handling request and response
  • Supports the Promise API
  • Transform request and response data
  • Abort requests by TimeoutAbortController
  • Automatic transforms for JSON response data, and supports custom transformer
  • Automatic data object serialization to multipart/form-data and x-www-form-urlencoded body encodings

Install

NPM

pnpm add requete
yarn add requete
npm i -S requete

CDN

<!-- using jsdelivr -->
<script src="https://cdn.jsdelivr.net/npm/requete/index.umd.min.js"></script>
<!-- or using unpkg -->
<script src="https://unpkg.com/requete/index.umd.min.js"></script>

Usage

First, you can import requete and use it directly.

import requete from 'requete'

// Make a GET request
requete.get('https://httpbin.org/get')

// Make a POST request
requete.post('https://httpbin.org/post', { id: 1 })

You can also create an instance and specify request configs by calling the create() function:

import { create } from 'requete'

const requete = create({ baseURL: 'https://httpbin.org' })

// Make a GET request
requete
  .get<IData>('/post')
  .then((r) => r.data)
  .catch((error) => {
    console.log(error) // error as `RequestError`
  })

For Nodejs (commonjs):

const requete = require('requete')

// use default instance
requete.get('https://httpbin.org/post')

// create new instance
const http = requete.create({ baseURL: 'https://httpbin.org' })
// Make a POST request
http.post('/post', { id: 1 })

For browsers:

  • UMD
<script src="https://cdn.jsdelivr.net/npm/requete"></script>

<script>
  // use default instance
  requete.get('https://httpbin.org/get')

  // create new instance
  const http = requete.create()
</script>
  • ESM: by index.browser.mjs
<script type="module">
  import requete from 'https://cdn.jsdelivr.net/npm/requete/index.browser.mjs'

  requete.get('https://httpbin.org/get')
</script>
  • ESM: by importmap
<script type="importmap">
  {
    "imports": {
      "requete": "https://cdn.jsdelivr.net/npm/requete/index.mjs",
      "requete/adapter": "https://cdn.jsdelivr.net/npm/requete/adapter.mjs",
      "requete/shared": "https://cdn.jsdelivr.net/npm/requete/shared.mjs"
    }
  }
</script>
<script type="module">
  import { create } from 'requete'

  const requete = create({ baseURL: 'https://httpbin.org' })

  requete.get('/get')
</script>

Request Methods

The following aliases are provided for convenience:

requete.request<D = any>(config: IRequest): Promise<IContext<D>>
requete.get<D = any>(url: string, config?: IRequest): Promise<IContext<D>>
requete.delete<D = any>(url: string, config?: IRequest): Promise<IContext<D>>
requete.head<D = any>(url: string, config?: IRequest): Promise<IContext<D>>
requete.options<D = any>(url: string, config?: IRequest): Promise<IContext<D>>
requete.post<D = any>(url: string, data?: RequestBody, config?: IRequest): Promise<IContext<D>>
requete.put<D = any>(url: string, data?: RequestBody, config?: IRequest): Promise<IContext<D>>
requete.patch<D = any>(url: string, data?: RequestBody, config?: IRequest): Promise<IContext<D>>

Example:

import { create } from 'requete'

const requete = create({ baseURL: 'https://your-api.com/api' })

// Make a GET request for user profile with ID
requete
  .get<IUser>('/users/profile?id=123')
  .then((r) => r.data)
  .catch(console.error)
  .finally(() => {
    // always executed
  })

// or use `config.params` to set url search params
requete.get<IUser>('/users/profile', { params: { id: '123' } })
requete.get<IUser>('/users/profile', { params: 'id=123' })

// Make a POST request for update user profile
requete.post('/users/profile', { id: '123', name: 'Jay Chou' })
// or use `requete.request`
requete.request({
  url: '/users/profile',
  method: 'POST'
  data: { id: '123', name: 'Jay Chou' },
})

requete.delete('/users/profile/123')

requete.put('/users/profile/123', { name: 'Jay Chou' })

Use Middleware

requete.use for add a middleware function to requete. It returns this, so is chainable.

  • The calling order of middleware should follow the Onion Model. like Koa middleware.
  • ctx is the requete context object, type IContext. more information in here.
  • next() must be called asynchronously in middleware
  • Throwing an exception in middleware will break the middleware execution chain.
  • Even if ctx.ok === false, there`s no error will be thrown in middleware.
requete
  .use(async (ctx, next) => {
    const token = getToken()
    // throw a `RequestError` if unauthorize
    if (!token) ctx.throw('unauthorize')
    // set Authorization header
    else ctx.set('Authorization', token)

    // wait for request responding
    await next()

    // when unauthorized, re-authenticate.
    if (ctx.status === 401) reauthenticate()
  })
  .use((ctx, next) =>
    next().then(() => {
      // throw a `RequestError` and break the subsequent execution
      if (!ctx.data.some_err_code === '<error_code>') {
        ctx.throw('Server Error')
      }
    })
  )

Request Config

Config for create instance.

create(config?: RequestConfig)

interface RequestConfig {
  baseURL?: string
  /** request timeout (ms) */
  timeout?: number
  /** response body type */
  responseType?: 'json' | 'formData' | 'text' | 'blob' | 'arrayBuffer'
  /** A string indicating how the request will interact with the browser's cache to set request's cache. */
  cache?: RequestCache
  /** A string indicating whether credentials will be sent with the request always, never, or only when sent to a same-origin URL. Sets request's credentials. */
  credentials?: RequestCredentials
  /** A Headers object, an object literal, or an array of two-item arrays to set request's headers. */
  headers?: HeadersInit
  /** A cryptographic hash of the resource to be fetched by request. Sets request's integrity. */
  integrity?: string
  /** A boolean to set request's keepalive. */
  keepalive?: boolean
  /** A string to indicate whether the request will use CORS, or will be restricted to same-origin URLs. Sets request's mode. */
  mode?: RequestMode
  /** A string indicating whether request follows redirects, results in an error upon encountering a redirect, or returns the redirect (in an opaque fashion). Sets request's redirect. */
  redirect?: RequestRedirect
  /** A string whose value is a same-origin URL, "about:client", or the empty string, to set request's referrer. */
  referrer?: string
  /** A referrer policy to set request's referrerPolicy. */
  referrerPolicy?: ReferrerPolicy
  /** enable logger or set logger level # */
  verbose?: boolean | number
  /**
   * parse json function
   * (for transform response)
   * @default JSON.parse
   */
  toJSON?(body: string): any
}

config.verbose is used to toggle the logger output.

  • set true or 2: output info and error level
  • set 1: output error level
  • set false or 0 or not set: no output

Config for request methods.

requete.request(config?: IRequest)

interface IRequest extends RequestConfig {
  url: string
  /**
   * A string to set request's method.
   * @default GET
   */
  method?: Method
  /** A string or object to set querystring of url */
  params?: string | Record<string, any>
  /** request`s body */
  data?: RequestBody
  /**
   * A TimeoutAbortController to set request's signal.
   * @default new TimeoutAbortController(timeout)
   */
  abort?: TimeoutAbortController | null
  /** specify request adapter */
  adapter?: Adapter
  /** flexible custom field */
  custom?: any
}

Request Config Defaults

You can specify request config defaults globally, that will be applied to every request. And the Requete.defaults is defined here.

import { Requete } from 'requete'

Requete.defaults.baseURL = 'https://your-api.com'
Requete.defaults.timeout = 60000
Requete.defaults.responseType = 'json'
Requete.defaults.headers = { 'X-Request-Id': 'requete' }

Response Typings

The response for a request is a context object, specifically of type IContext, which contains the following information.

interface IResponse<Data = any> {
  headers: Headers
  ok: boolean
  redirected: boolean
  status: number
  statusText: string
  type: ResponseType
  url: string
  data: Data
  /** response text when responseType is `json` or `text` */
  responseText?: string
}

interface IContext<Data = any> extends IResponse<Data> {
  /**
   * request config.
   * and empty `Headers` object as default
   */
  request: IRequest & { headers: Headers }

  /**
   * set request headers
   * *And header names are matched by case-insensitive byte sequence.*
   * @throws {RequestError}
   */
  set(headerOrName: HeadersInit | string, value?: string | null): this

  /**
   * Add extra params to `request.url`.
   * If there are duplicate keys, then the original key-values will be removed.
   */
  params(params: RequestQuery): this

  /**
   * get `ctx.request.abort`,
   * and **create one if not exist**
   * @throws {RequestError}
   */
  abort(): TimeoutAbortController

  /** throw {@link RequestError} */
  throw(e: string | Error): void

  /**
   * Assign to current context
   */
  assign(context: Partial<IContext>): void

  /**
   * Replay current request
   * And assign new context to current, with replay`s response
   */
  replay(): Promise<void>
}

In middleware, the first argument is ctx of type IContext. You can call methods such as ctx.set, ctx.throw, ctx.abort before sending the request (i.e., before the await next() statement). Otherwise, if these methods are called in other cases, a RequestError will be thrown.

ctx.set(key, value)

set one header of request. And header names are matched by case-insensitive byte sequence.

ctx.set(object)

set multi headers of request.

ctx.params(params)

Add extra params to request.url.
If there are duplicate keys, then the original key-values will be removed.

ctx.abort()

Return the current config.abort, and create one if not exist

ctx.throw(error)

It is used to throw a RequestError

ctx.assign(context)

It is used to assign new context object to current. (Object.assign)

ctx.replay()

It is used to replay the request in middleware or other case.
After respond, will assign new context to current, with replay`s response, And will add counts of replay in ctx.request.custom.replay.

Examples:

const Auth = {
  get token() {
    return localStorage.getItem('token')
  },
  set token(value) {
    return localStorage.setItem('token', value)
  },
  authenticate: () =>
    requete.post('/authenticate').then((r) => {
      Auth.token = r.data.token
    }),
}

requete.use(async (ctx, next) => {
  ctx.set('Authorization', `Bearer ${Auth.token}`)

  await next()

  // when unauthorized, re-authenticate
  // Maybe causes dead loop if always respond 401
  if (ctx.status === 401) {
    await Auth.authenticate()
    // replay request after re-authenticated.
    await ctx.replay()
  }
})

RequestError

RequestError inherits from Error, contains the request context information.

It should be noted that all exceptions in requete are RequestError.

class RequestError extends Error {
  name = 'RequestError'
  ctx: IContext

  constructor(errMsg: string | Error, ctx: IContext)
}

Example

If needed, you can import RequestError it from requete

import { RequestError } from 'requete'

throw new RequestError('<error message>', ctx)
throw new RequestError(new Error('<error message>'), ctx)

Throw RequestError in requete middleware

// in requete middleware
ctx.throw('<error message>')

Caught RequeteError in request

// promise.catch
requete.post('/api').catch((e) => {
  console.log(e.name) // "RequestError"
  console.log(e.ctx.status) // response status
  console.log(e.ctx.headers) // response header
})

// try-catch
try {
  await requete.post('/api')
} catch (e) {
  console.log(e.name) // "RequestError"
  console.log(e.ctx.status) // response status
  console.log(e.ctx.headers) // response header
}

TimeoutAbortController

it is used to auto-abort requests when timeout, and you can also call abort() to terminate them at any time. It is implemented based on AbortController.

In the requete configuration, you can add the TimeoutAbortController through the abort field.
It should be noted that if you set the timeout field in config and unset the abort field, requete will add the TimeoutAbortController by default to achieve timeout termination.

If the target browser does not support AbortController, please add a polyfill before using it.

class TimeoutAbortController {
  /** if not supported, it will throw error when `new` */
  static readonly supported: boolean

  /** timeout ms */
  constructor(timeout: number)

  get signal(): AbortSignal

  abort(reason?: any): void

  /** clear setTimeout */
  clear(): void
}

Example

import { TimeoutAbortController } from 'requete'

/** By `abort` config */
const controller = new TimeoutAbortController(5000)
requete
  .get('https://httpbin.org/delay/10', { abort: controller })
  .catch((e) => {
    console.error(e) // "canceled"
  })
controller.abort('canceled') // you can abort request

/** By `timeout` config */
requete.get('https://httpbin.org/delay/10', { timeout: 5000 })

Request Adapter

There are two request adapters in requete: FetchAdapter, XhrAdapter.

  • In Browser: using FetchAdapter as default, and XhrAdapter is used as a fallback.
  • In Node.js: using FetchAdapter.

Of course, you can also customize which adapter to use by declaring the adapter field in config. For example, in browser environment, when obtaining download or upload progress events, you can choose to use the XhrAdapter. (like Axios)

import requete, { XhrAdapter } from 'requete'

requete.get('/download-or-upload', {
  adapter: new XhrAdapter({ onDownloadProgress(e) {}, onUploadProgress(e) {} }),
})

Additionally, requete also supports custom adapters by inheriting the abstract class Adapter and implementing the request method.

abstract class Adapter {
  abstract request(ctx: IContext): Promise<IResponse>
}

Example

// CustomAdapter.ts

import { Adapter } from 'requete/adapter'

export class CustomAdapter extends Adapter {
  async request(ctx: IContext) {
    // do request

    return response
  }
}

Polyfills

If needed, you can directly import requete/polyfill. It includes polyfills for Headers and AbortController.

requete/polyfill will determine whether to add polyfills based on the user's browser.

In ES Module:

import 'requete/polyfill'

In Browser:

<!-- using jsdelivr -->
<script src="https://cdn.jsdelivr.net/npm/requete/polyfill.umd.min.js"></script>
<!-- using unpkg -->
<script src="https://unpkg.com/requete/polyfill.umd.min.js"></script>