An even simpler wrapper around native Fetch to strip boilerplate from your fetching code!

Overview

itty-fetcher

Version Bundle Size Build Status Coverage Status NPM Weekly Downloads Open Issues

Discord GitHub Repo stars Twitter

Super lightweight (~450 bytes) wrapper to simplify native fetch calls using any HTTP method (existing or imagined).

Features

  • Fully typed/TypeScript support
  • Automatically parses responses (optional)
  • Automatically serializes object payloads
  • Accepts any HTTP method (including user-defined)
  • 404, 400, 500, errors actually throw to allow easier catching
  • still allows any native fetch options (including headers, etc) to be sent

Simple Usage

import { fetcher } from 'itty-fetcher'

// create a basic fetcher with default options
const basics = fetcher()

// skips the body parsing for normal GET requests
await basics.get('https://api.kittens.com/v1/names/?max=2') // ['Fluffy', 'Mittens']

// set a base for simplifying repeated calls
const api = fetcher({ base: 'https://api.kittens.com/v1/' })

// then use it... base will be prepended to urls
await api.get('names/?max=2') // ['Fluffy', 'Mittens']

// automatic handle sending payloads (no need to stringify and set headers)
await api.post('create-a-cat', { name: 'Halsey', age: 3 }) // { id: 'Q4AW', name: 'Halsey', age: 3 }

// use any conceivable HTTP method
api.put('kitten/13', { name: 'Different Cat' }) // sends using PUT method
api.foo('kitten/13', { name: 'Different Cat' }) // sends using FOO method

// ERROR HANDLING: 400, 404, 500, etc will actually throw, allowing an easy catch
api
  .get('not-a-valid-path')
  .catch(({ status, message }) => {
    console.log('received a status', status, 'error with message:', message)
  })

Why yet another fetching library?

We've all done this countless times in our apps...

We want to make a nice, lightweight app that (of-course) talks to some API. We could import a full-featured fetch library like axios, but we want to keep our bundle size down, right?

So we just write some basic native fetch statements. That's not hard... we've tread this ground before! Of course as the project grows a bit, we start to become bothered by the repeated boilerplate of setting headers, checking for errors, translating response bodies, etc.

So what do we do?

Why, we write a little abstraction layer of course! Just like this one, but probably a bit bigger.

So who is this for?

This is not a kitchen-sink sort of library. It will intentionally not cover every edge case. By only handling a variety of the most common use-cases, I can keep the bundle size down to [likely] smaller than the code you would have written yourself, making it a no-brainer for easy inclusion into your projects.

Need more advanced fetch handling? Perhaps try a different library (or stick to native fetch and handle the edge case manually)!

ADVANCED USAGE

// skipping autoParse returns full Response control
const unparsed = fetcher({ autoParse: false })

unparsed
  .get('https://api.kittens.com/v1/names/?max=2')
  .then(response => {
    if (response.ok) return response.json()
  })

// can send all native fetch options through in 3rd param
fetcher()
  .post('https://api.kittens.com/v1/names/?max=2',
        { payload: 'is second param' },
        {
          credentials: 'same-origin',
          cache: 'no-cache',
          headers: {
            ['my-fancy-header']: 'will be sent'
          }
        }
  )

Installation

npm install itty-fetcher

API

fetcher(options?: FetcherOptions): FetcherType

Returns a fetcher object, with method calls (like .get, .post, etc) mapped to fetch commands.

Option Type(s) Default Description
autoParse boolean true By default, all responses are parsed to JSON/text/etc. To access the Response directly, set this to false.
base string '' Use this to prefix all future fetch calls, for example { base: "https://api.foo.bar/v1" }, allows future calls such as fetcher.get('kittens/14') to work by automatically prepending the base URL.

Special Thanks

I have to thank my friends and colleagues that helped me through the idea itself, implementation details, and importantly... made it possible for me to muck through making this a TS-first library. Huge thanks for that!!

Comments
  • Default Base and Relative Paths

    Default Base and Relative Paths

    The default behavior here in-browser (base: '') results in a base url relative to your current pathname (on this page, relative to /kwhitley/itty-fetcher/issues/new). This has real fun results when you simply concatenate other stuff (whatever/test) for instance.

    In that case I'd be fetching against /kwhitley/itty-fetcher/issues/whatever/test (note the missing new path segment). This gets more confusing since if your route happens to end in a slash (.../new/, which is allowed and navigable on github) then it does not strip the new (since it's relative to the /). Even worse, if you have a <base> tag with a value set: <base href="http://example.com" target="_blank"> it will be relative to the href there, which might be completely different (in this case http://example.com/whatever/test).

    All that can be better controlled with the second parameter to URL: new URL('whatever/test', 'http://www.github.com/kwhitley/itty-fetcher/'). I see a few options for solving this:

    1. Test and warn (if (!base.endsWith('/')) console.warn("Your base does not end in/and so ...");)
    2. A template literal type for base: base: `${string}/
    3. Using the URL constructor to combine the base and path (downside: the vase will need to be a full URL (including protocol [http/https]) so you may have to do more munging than you want. This allows for easy query param tweaking if you want your base to include share query params (like an Authoriation, for example).

    Sorry for the wall of text, feel free to close. Just stuff I've been stewing on since our twitter discussion.

    opened by Crisfole 14
  • Pass FormData in as-is

    Pass FormData in as-is

    • Add support for passing in FormData as-is instead of JSON.stringifying it as we do now. Should only add a few bytes to the final bundle
    • Add tests for passing in FormData
    • Big refactoring of tests

    Closes #10

    opened by danawoodman 13
  • [#17] Allow manually sending

    [#17] Allow manually sending "body" in request init

    Other changes:

    • Add test for changing content-type via request init to verify behavior.
    • Change vitest test environment to jsdom. This removes the need for importing isometric-fetch and brings the test env closer to the DOM which seems a safe baseline test env.

    Closes #17

    opened by danawoodman 6
  • Add safe checking of payload data type to prevent undefined errors

    Add safe checking of payload data type to prevent undefined errors

    This fixed issues where Blob/FormData are being reported as undefined. It also makes our auto content-type/JSON.stringify code work more reliably.

    Also added checking for Uint8Array. May eventually need to support more content types.

    Thanks to @Crisfole for the tip on using typeof FormData !== "undefined"!

    • Export all common types so consumers can use them if needed

    Closes #16 Closes #21

    opened by danawoodman 5
  • FIX: defined headers should be honored *over* injected headers

    FIX: defined headers should be honored *over* injected headers

    Expected Behavior

    • If I set a content-type header, I should expect it to be honored, no matter what
    • If I set a body (manually) in the options, I should expect it to be honored, no matter what

    Actual Behavior

    • If I set a content-type header, application/json will be sent anyway
    • If I set a body (manually) in the options, the payload is sent instead

    Use Case

    I am attempting to send blob data through a fetcher instance. I can do this with native fetch, but not itty-fetcher:

    const blob = new Blob(["This is some important text"], { type: "text/plain" });
    
    fetch('https://ity.sh/create', { method: 'POST', body: blob })
    
    bug 
    opened by kwhitley 5
  • Actions expect form-encoded data (received application/json) error in SvelteKit form handler

    Actions expect form-encoded data (received application/json) error in SvelteKit form handler

    With v0.6.1 itty-fetcher does not appear to work with form actions. POSTing setting value as JSON for some inexplicable reason.

    const data = new FormData();
    data.set("file", new File(["hi"], "foo.txt"));
    fetcher()
    	.post("/preview?/upload", data)
    	.then(console.log)
    	.catch(console.error);
    

    Fails with Actions expect form-encoded data (received application/json)

    opened by danawoodman 3
  • [#25] Don't stringify the prototype of a payload

    [#25] Don't stringify the prototype of a payload

    Previous code threw because TypedArrays/ArrayBuffers will fail when you attempt to Stringify their prototype, like Uin8Array.

    Instead we get the constructor of the prototype and check its name, which is also a bit more elegant and explicity as a bonus.

    Closes #25

    opened by danawoodman 2
  • Invert the type checking logic of the payload in order to determine whether to stringify or not

    Invert the type checking logic of the payload in order to determine whether to stringify or not

    This change means that isn't a base type of undefined, string, number, array or object will be passed as-is and the { "content-type": "application/json" } header will be omitted.

    This means we don't need to do the individual type checking that we had before, which was really fragile and limited.

    opened by danawoodman 2
  • FormData handling

    FormData handling

    I believe right now sending FormData is broken as we are always stringifying the payload.

    I believe we want to check if the payload is FormData and if so, pass it as-is:

    payload: payload instanceof FormData ? payload : JSON.stringify(payload)
    

    We should add tests to assert this expected behavior.

    This is a placeholder for that work.

    opened by danawoodman 2
  • Pass payload data to query params for GET requests

    Pass payload data to query params for GET requests

    Added tests. Note that I had to move the tests into a beforeEach so I can assert against the calls otherwise I get all them mock calls in the test.

    Adds about 110 bytes, not sure if it is possible to reduce further.

    opened by danawoodman 2
  • isomorphic-fetcher is a non-existent package on NPM

    isomorphic-fetcher is a non-existent package on NPM

    Should be isomorphic-fetch in your package.json dependencies and not isomorphic-fetcher.

    https://socket.dev/npm/package/isomorphic-fetch

    Note also, this package is unmaintained, has no source code repo, and no README.

    itty-fetcher on  v0.x via 🤖 v16.13.2 via 🎁 v0.1.1  ❯
    npm i
    npm ERR! code E404
    npm ERR! 404 Not Found - GET https://registry.npmjs.org/isomorphic-fetcher - Not found
    npm ERR! 404
    npm ERR! 404  'isomorphic-fetcher@^3.0.0' is not in this registry.
    npm ERR! 404
    npm ERR! 404 Note that you can also install from a
    npm ERR! 404 tarball, folder, http url, or git url.
    
    npm ERR! A complete log of this run can be found in:
    npm ERR!     /Users/glenn/.npm/_logs/2022-09-11T05_13_47_579Z-debug-0.log
    
    opened by grempe 2
  • Request for options access to AbortControllers

    Request for options access to AbortControllers

    Love the simplicity and syntax of this library! The one thing preventing me from using it some production code is a way to work with Abort Controllers. Do you have a preferred method with this library (maybe I'm over looking something) or is that a feature worth adding to your roadmap?

    opened by jkauszler 0
Owner
Kevin R. Whitley
I write things to make your code shorter and more readable. ❤️
Kevin R. Whitley
A simple nodejs module which is wrapper around solc that allows you to compile Solidity code

Simple Solidity Compiler It's a simple nodejs module which is wrapper around solc that allows you to compile Solidity code and get the abi and bytecod

King Rayhan 4 Feb 21, 2022
Makes Base64 en & -decoding simpler as it is.

jQuery.base64.js (feel free to write a bit about it) You can check for btoa and atob support and refer to jQuery.base64 if (!window.btoa) window.btoa

Yannick Albert 120 Sep 23, 2022
Thin wrapper around Rant-Lang for Obsidian.md

Obsidian Rant-Lang Thin wrapper around the Rant language Rust crate to be used in Obsidian. "Rant is a high-level procedural templating language with

Leander Neiss 10 Jul 12, 2022
A thin wrapper around arweave-js for versioned permaweb document management.

?? ar-wrapper A thin wrapper around arweave-js for versioned permaweb document management. Helps to abstract away complexity for document storage for

verses 8 May 12, 2022
A nuxt 2 wrapper around derrickreimer/fathom-client to be able to use usefathom.com in all its glory

This package is a nuxt 2 wrapper around derrickreimer/fathom-client to be able to use usefathom.com in all its glory. Thanks to @derrickreimer for this framework agnostic library ❤️‍??.

wellá 6 Aug 18, 2022
A maybe slightly safer-ish wrapper around eval Function constructors

evalish A maybe slightly safer-ish wrapper around eval Function constructors Please maybe try something else first.. Please. evalish is a small helper

Phil Pluckthun 24 Sep 6, 2022
A jQuery plugin wrapper around Bootstrap Alerts, to create Notifications (Toasts)

bootstrap-show-notification A jQuery plugin wrapper around Bootstrap 4 Alerts, to show them as toasts (also called notifications) dynamically from Jav

Stefan Haack 10 Aug 22, 2022
A wrapper around IPFS for speeding up the loading of web3 frontend applications.

ipfs-wrapper A wrapper around ipfs-core for speeding up the loading of web3 frontend applications. Used on Blogchain. Requirements NodeJS v14.5.0 or h

Capsule Social 15 Sep 14, 2022
A maybe slightly safer-ish wrapper around eval Function constructors

evalish A maybe slightly safer-ish wrapper around eval Function constructors Please maybe try something else first.. Please. evalish is a small helper

0no.co 22 Aug 21, 2022
⚡⚙️ Fast prototyping with template engines and integrated frontend tools. Vituum is a small wrapper around Vite.

⚡ ⚙️ Vituum Still in early development. Fast prototyping with template engines and integrated frontend tools ⚡ Vite integrated ??️ Fast prototyping ??

Vituum 103 Jan 3, 2023
Wen? Now! A library to simplify your Web3 data fetching.

Wen Connect? Now! Minimalistic library for Web3 user interfaces. Seamless connection flows to Metamask. Stateless sessions that work anywhere (client,

Guillaume Bibeau-laviolette 20 Jul 20, 2022
Receive crypto payments from anywhere around the world, options including native tokens (MATIC, ETHER,BUSD), Tokens (USDT,BUSD), NFTs and more.

Receive payments for service rendered in crypto using different options. Go borderless with bonpay, gain access to varities of crypto assets, safe and

Johnson awah Alfred 6 Nov 11, 2022
portfolio-project is a npm package to automatically update your projects section in your portfolio website. It will fetch the selected repositories directly from your GitHub account.

portfolio-project Those days of manually updating portfolio website after every new project made are gone ⚡ Yesss . . . you read that right. ?? portfo

Gaurav Gulati 15 Aug 3, 2021
A Cloudflare Worker for fetching data from Twitter User API.

cloudflare-worker-twitter This is a Cloudflare Worker for fetching data from Twitter User API. ❔ About this project I created this Worker to hide my A

Arda Soytürk 12 Oct 1, 2022
Simple weather app written in HTML, CSS, and JavaScript using the OpenWeather API for fetching weather and geolocation information

Description Simple weather app written in HTML, CSS, and JavaScript using the OpenWeather API for fetching weather and geolocation information. Acknow

Gleb Korzan 4 Feb 23, 2022
A crawler that crawls the site's internal links, fetching information of interest to any SEO specialist to perform appropriate analysis on the site.

Overview ?? It is a module that crawls sites and extracts basic information on any web page of interest to site owners in general, and SEO specialists

Yazan Zoghbi 2 Apr 22, 2022
A crawler that crawls the site's internal links, fetching information of interest to any SEO specialist to perform appropriate analysis on the site.

Overview ?? It is a module that crawls sites and extracts basic information on any web page of interest to site owners in general, and SEO specialists

Yazan Zoghbi 2 Apr 22, 2022
A cli tool for fetching information about countries.

countryfetch A cli tool for fetching information about countries. It uses https://restcountries.com/ API for backend. Dependencies DENO Installation D

Pridon Tetradze 119 Dec 24, 2022