Giter VIP home page Giter VIP logo

get-it's Introduction

get-it

npm stat npm version gzip size size

Generic HTTP request library for node.js (>= 14) and modern browsers.

Motivation

We wanted an HTTP request library that worked transparently in Node.js and browsers with a small browser bundle footprint.

To be able to use the same library in a range of different applications with varying requirements, but still keep the bundle size down, we took inspiration from http-client which cleverly composes functionality into the client.

Features

Using a middleware approach, get-it has the following feature set:

  • Promise, observable and low-level event-emitter patterns
  • Automatic retrying with customizable number of attempts and filtering functionality
  • Cancellation of requests
  • Configurable connect/socket timeouts
  • Automatic parsing of JSON responses
  • Automatic stringifying of JSON request bodies
  • Automatic gzip unwrapping in Node
  • Automatically prepend base URL
  • Automatically follow redirects (configurable number of retries)
  • Upload/download progress events
  • Treat HTTP status codes >=400 as errors
  • Debug requests with environment variables/localStorage setting

Usage

How get-it behaves depends on which middleware you've loaded, but common to all approaches is the setup process.

// Import the core get-it package, which is used to generate a requester
import {getIt} from 'get-it'

// And import whatever middleware you want to use
import {base, jsonResponse, promise} from 'get-it/middleware'

// Now compose the middleware you want to use
const request = getIt([base('https://api.your.service/v1'), jsonResponse()])

// You can also register middleware using `.use(middleware)`
request.use(promise())

// Now you're ready to use the requester:
request({url: '/projects'})
  .then((response) => console.log(response.body))
  .catch((err) => console.error(err))

In most larger projects, you'd probably make a httpClient.js or similar, where you would instantiate the requester and export it for other modules to reuse.

Options

  • url - URL to the resource you want to reach.
  • method - HTTP method to use for request. Default: GET, unless a body is provided, in which case the default is POST.
  • headers - Object of HTTP headers to send. Note that cross-origin requests in IE9 will not be able to set these headers.
  • body - The request body. If the jsonRequest middleware is used, it will serialize to a JSON string before sending. Otherwise, it tries to send whatever is passed to it using the underlying adapter. Supported types:
    • Browser: string, ArrayBufferView, Blob, Document, FormData (deprecated: ArrayBuffer)
    • Node: string, buffer, ReadStream
  • bodySize - Size of body, in bytes. Only used in Node when passing a ReadStream as body, in order for progress events to emit status on upload progress.
  • timeout - Timeout in millisecond for the request. Takes an object with connect and socket properties.
  • maxRedirects - Maximum number of redirects to follow before giving up. Note that this is only used in Node, as browsers have built-in redirect handling which cannot be adjusted. Default: 5
  • rawBody - Set to true to return the raw value of the response body, instead of a string. The type returned differs based on the underlying adapter:
    • Browser: ArrayBuffer
    • Node: Buffer

Return values

By default, get-it will return an object of single-channel event emitters. This is done in order to provide a low-level API surface that others can build upon, which is what the promise and observable middlewares do. Unless you really know what you're doing, you'll probably want to use those middlewares.

Response objects

get-it does not expose the low-level primitives such as the XMLHttpRequest or http.IncomingMessage instances. Instead, it provides a response object with the following properties:

{
  // string (ArrayBuffer or Buffer if `rawBody` is set to `true`)
  body: 'Response body'
  url: 'http://foo.bar/baz',
  method: 'GET',
  statusCode: 200,
  statusMessage: 'OK',
  headers: {
    'Date': 'Fri, 09 Dec 2016 14:55:32 GMT',
    'Cache-Control': 'public, max-age=120'
  }
}

Promise API

For the most part, you simply have to register the middleware and you should be good to go. Sometimes you only need the response body, in which case you can set the onlyBody option to true. Otherwise the promise will be resolved with the response object mentioned earlier.

import {getIt} from 'get-it'
import {promise} from 'get-it/middleware'
const request = getIt([promise({onlyBody: true})])

request({url: 'http://foo.bar/api/projects'})
  .then((projects) => console.log(projects))
  .catch((err) => console.error(err))

Cancelling promise-based requests

With the Promise API, you can cancel requests using a cancel token. This API is based on the Cancelable Promises proposal, which was at Stage 1 before it was withdrawn.

You can create a cancel token using the CancelToken.source factory as shown below:

import {promise} from 'get-it/middleware'
const request = getIt([promise()])

const source = promise.CancelToken.source()

request
  .get({
    url: 'http://foo.bar/baz',
    cancelToken: source.token,
  })
  .catch((err) => {
    if (promise.isCancel(err)) {
      console.log('Request canceled', err.message)
    } else {
      // handle error
    }
  })

// Cancel the request (the message parameter is optional)
source.cancel('Operation canceled by the user')

Observable API

The observable API requires you to pass an Observable-implementation that you want to use. Optionally, you can register it under the global Observable, but this is not recommended.

import {getIt} from 'get-it'
import {observable} from 'get-it/middleware'
import zenObservable from 'zen-observable'

const request = getIt()

request.use(
  observable({
    implementation: zenObservable,
  }),
)

const observer = request({url: 'http://foo.bar/baz'})
  .filter((ev) => ev.type === 'response')
  .subscribe({
    next: (res) => console.log(res.body),
    error: (err) => console.error(err),
  })

// If you want to cancel the request, simply unsubscribe:
observer.unsubscribe()

It's important to note that the observable middleware does not only emit response objects, but also progress events. You should always filter to specify what you're interested in receiving. Every emitted value has a type property.

Upcoming features

  • Developer-friendly assertions that are stripped in production to reduce bundle size and performance
  • Authentication (basic)
  • Stream response middleware?

Prior art

This module was inspired by the great work of others:

License

MIT-licensed. See LICENSE.

Release new version

Run the "CI & Release" workflow. Make sure to select the main branch and check "Release new version".

Semantic release will only release on configured branches, so it is safe to run release on any branch.

get-it's People

Contributors

bjoerge avatar dependabot[bot] avatar ecospark[bot] avatar github-actions[bot] avatar judofyr avatar renovate[bot] avatar rexxars avatar semantic-release-bot avatar sgulseth avatar stipsan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

get-it's Issues

Ability to set max response size

It'd be nice to have a middleware that could limit the number of response size.
If a content-length is received, that could be used, otherwise just count bytes from the stream.

Don't think we could get this working with the XHR adapter, but should work in Node, and possibly a future fetch adapter (now that streams are implemented/on the way).

8.1.0 crashing on vercel

Just had deployments starting to crash on vercel after get-it versions for sanity packages were bumped to 8.10 from 8.0.11
I confirmed the break to be in this version by overriding just get-it back to 8.0.11.
Here's the stack trace from the function failure:

TypeError: Right-hand side of 'instanceof' is not an object
    at (../../node_modules/.pnpm/[email protected]/node_modules/get-it/dist/index.browser.js:185:0)
    at (../../node_modules/.pnpm/[email protected]/node_modules/get-it/dist/index.browser.js:283:0)
    at (../../node_modules/.pnpm/[email protected]/node_modules/get-it/dist/index.browser.js:67:0)
    at (../../node_modules/.pnpm/[email protected]/node_modules/get-it/dist/index.browser.js:33:0)
    at (../../node_modules/.pnpm/[email protected]/node_modules/get-it/dist/middleware.browser.js:255:0)
    at (../../node_modules/.pnpm/[email protected]/node_modules/rxjs/dist/cjs/internal/Observable.js:41:0)
    at (../../node_modules/.pnpm/[email protected]/node_modules/rxjs/dist/cjs/internal/Observable.js:35:0)
    at (../../node_modules/.pnpm/[email protected]/node_modules/rxjs/dist/cjs/internal/util/errorContext.js:22:0)
    at (../../node_modules/.pnpm/[email protected]/node_modules/rxjs/dist/cjs/internal/Observable.js:26:0)
    at (../../node_modules/.pnpm/@[email protected]/node_modules/@sanity/client/dist/index.browser.js:812:0)

Add support for mocking responses

With the middleware architecture in place, it shouldn't be too difficult to add support for mocking responses. That would be a nice addition that would ease testing in a lot of scenarios, in a way that would work in both browsers and on the server.

Cannot find module 'get-it/lib-node'

The module is required by @sanity/client

Using npm 6.4.1 and Brunch 2.10.17.
Npm compiles the module creating two folders 'lib' and 'lib-node'. However, main entry file index.js points just to './lib-node'

module.exports = require('./lib-node')

This causes module to fail in browser context, where only '/lib' is bundled. However, everything works just fine when manually changed to:

module.exports = require('./lib')

Is this a problem with module or more probably with bundler?

Retry triggers response callback

There is a bug in the implementation of the error handling.

When an error is encountered (for instance, in the case of a network error or a timeout), the way we wanted to handle retries is to "catch" the error and signal to the requester that it has been handled by simply not returning the error.

The issue is that if no error is returned, the response channel is published to instead.

We need to figure out a way for middleware to be able to say "I've got this, but the request isn't done yet". There aren't currently any other middlewares that utilitize the error capturing, so perhaps we could just leave it up to the middleware to publish a message on the response channel if it wants to somehow trigger the response. Need to think on this some more.

Security concern

Hey there!

I belong to an open source security research community, and a member (@ranjit-git) has found an issue, but doesn’t know the best way to disclose it.

If not a hassle, might you kindly add a SECURITY.md file with an email, or another contact method? GitHub recommends this best practice to ensure security issues are responsibly disclosed, and it would serve as a simple instruction for security researchers in the future.

Thank you for your consideration, and I look forward to hearing from you!

(cc @huntr-helper)

Promise: Add option to resolve only body

Sometimes, all you care about is the response body. For instance, if you are using the httpErrors-middleware, errors are handled "for you", and thus you don't need access to the status code. So it would be great to have a... resolveWithBody-option (name suggestions are welcome) that only resolves the promise with the response body. This reads much nicer with async/await and friends:

const projects = await request({ url: '/v1/projects' })

// vs

const projects = (await request({ url: '/v1/projects' })).body

Can't use with relative urls in a browser context

The following fails with Uncaught Error: "/some/endpoint" is not a valid URL:

const request = getIt([jsonResponse()])

request('/some/endpoint') 

Is there any way configure get-it to allow both relative urls and absolute urls?

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.


Using a curated preset maintained by


Sanity: The Composable Content Cloud

Pending Approval

These branches will be created by Renovate only once you click their checkbox below.

  • chore(deps): lock file maintenance

Awaiting Schedule

These updates are awaiting their schedule. Click on a checkbox to get an update now.

  • chore(deps): update dependency @sanity/pkg-utils to ^3.3.5
  • chore(deps): update dependency vite to v5.0.10
  • chore(deps): update vitest monorepo to ^1.0.4 (@vitest/coverage-v8, vitest)
  • chore(deps): update linters (@typescript-eslint/eslint-plugin, @typescript-eslint/parser, eslint, prettier)

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Detected dependencies

github-actions
.github/workflows/ci.yml
  • actions/checkout v4
  • actions/setup-node v4
  • actions/checkout v4
  • actions/setup-node v4
  • actions/checkout v4
  • actions/setup-node v4
  • actions/checkout v4
  • actions/setup-node v4
  • denoland/setup-deno v1
  • actions/checkout v4
  • actions/setup-node v4
  • actions/checkout v4
  • actions/setup-node v4
  • actions/checkout v4
  • actions/setup-node v4
.github/workflows/lock.yml
  • dessant/lock-threads v5@1bf7ec25051fe7c00bdd17e6a7cf3d7bfb7dc771
.github/workflows/prettier.yml
  • actions/checkout v4
  • actions/setup-node v4
  • actions/cache v3
  • tibdex/github-app-token v2@3beb63f4bd073e61482598c45c71c1019b59b73a
  • peter-evans/create-pull-request v5@153407881ec5c347639a548ade7d8ad1d6740e38
.github/workflows/test-next.yml
  • actions/checkout v4
  • actions/setup-node v4
npm
package.json
  • decompress-response ^7.0.0
  • follow-redirects ^1.15.2
  • into-stream ^6.0.0
  • is-plain-object ^5.0.0
  • is-retry-allowed ^2.2.0
  • is-stream ^2.0.1
  • parse-headers ^2.0.5
  • progress-stream ^2.0.0
  • tunnel-agent ^0.6.0
  • @edge-runtime/vm ^3.1.7
  • @sanity/pkg-utils ^3.3.2
  • @sanity/semantic-release-preset ^4.1.6
  • @types/follow-redirects ^1.14.4
  • @types/progress-stream ^2.0.5
  • @types/zen-observable ^0.8.7
  • @typescript-eslint/eslint-plugin ^6.13.2
  • @typescript-eslint/parser ^6.13.2
  • @vitest/coverage-v8 ^1.0.1
  • eslint ^8.55.0
  • eslint-config-prettier ^9.1.0
  • eslint-plugin-prettier ^5.0.1
  • eslint-plugin-simple-import-sort ^10.0.0
  • faucet ^0.0.4
  • happy-dom ^12.10.3
  • ls-engines ^0.9.1
  • node-fetch ^2.6.7
  • prettier ^3.1.0
  • prettier-plugin-packagejson ^2.4.7
  • rimraf ^5.0.1
  • typescript ^5.3.3
  • vite 5.0.6
  • vitest ^1.0.1
  • vitest-github-actions-reporter ^0.11.1
  • zen-observable ^0.10.0
  • node >=14.0.0
test-next/package.json
  • @types/react ^18.2.14
  • @types/react-dom ^18.2.6
  • next 14.0.4-canary.47
  • react ^18.2.0
  • react-dom ^18.2.0
  • typescript ^5.1.3

  • Check this box to trigger a request for Renovate to run again on this repository

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.