JSPM

  • Created
  • Published
  • Downloads 497
  • Score
    100M100P100Q107025F
  • License MIT

An ultra-tiny native fetch wrapper to clean up your API calls.

Package Exports

  • itty-fetcher

Readme


itty-fetcher

GitHub Version Bundle Size Build Status Coverage Status Issues Discord

Documentation  |   Discord


itty-fetcher is a lightweight wrapper around the native fetch API that eliminates the common boilerplate when making API calls.

✨ Key Features

  • Automatic - JSON parsing, payload serialization, HTTP error throwing, etc.
  • Composable - Set up your API/endpoint once, then call it cleanly
  • Human-Readable - Method calls that feel natural
    • fetcher().get('/users')
    • users.post({ name: 'Steve', age: 24 })
  • 100% TypeScript - Intelligent type inference with generics for request/response shapes
  • Universal - Works everywhere fetch is supported... or not (through polyfills)

...and of course itty, at under 650 bytes. We got you, fam.

Comparison

Before (native fetch):

const response = await fetch('/api/users', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ name: 'Alice' })
})

if (!response.ok) throw new Error(`${response.status}: ${response.statusText}`)

const user = await response.json()

After (itty-fetcher):

const user = await fetcher().post('/api/users', { name: 'Alice' })

Quick Start

Option 1: Import

import { fetcher } from 'itty-fetcher'

Option 2: Just copy this snippet:

let fetcher=(e,s)=>{let t="string"==typeof e?{base:e,...s}:e||{};return new Proxy((()=>{}),{get:(e,s)=>(...e)=>(async(e,s,t,r=("string"==typeof t[0]?t.shift():""),a=("GET"!=e?t.shift():null),n={...s,...t.shift(),method:e},o=new Headers(s.headers),i="string"==typeof a,f=s.base??"")=>{r=new URL((r.includes("://")?r:(f.includes?.("://")?f:globalThis.location?.href+"/"+f)+(r?"/"+r:"")).replace(/\/+/g,"/"));for(let e in n.query||{})r.searchParams.append(e,n.query[e]);n.body=a,a&&0!=n.encode&&(n.body=i?a:JSON.stringify(a),!i&&o.set("content-type","application/json"));for(let[e,s]of new Headers(n.headers||[]))o.set(e,s);let p=await(n.fetch||fetch)(new Request(r,{...n,headers:o})),c=p.ok?void 0:Object.assign(new Error(p.statusText),{status:p.status,response:p});if(n.parse??"json")try{p=await p[n.parse??"json"](),c&&"json"==(n.parse??"json")&&(c={...c,...p})}catch(e){!c&&(c=Object.assign(new Error(e.message),{status:p.status,response:p}))}for(let e of n.after||[])p=await e(p)??p;if(n.array)return[c,c?void 0:p];if(c)throw c;return p})(s.toUpperCase(),t,e)})};

Note: This will lose TypeScript support, but is great for adding to your browser console (via script extensions, etc).


Basic Usage

import { fetcher } from 'itty-fetcher'

// make simple requests
const users = await fetcher().get('https://ittysockets.io/stats')
await fetcher().post('/api/users', { name: 'Alice' })

// ...or create a reusable API client
const api = fetcher('https://api.example.com', {
  headers: { 'x-powered-by': 'itty.dev' }
})

// then use it
const newUser = await api.post('/users', { name: 'Katiya' })

Philosophy

Like any itty.dev project, this is not a kitchen-sink library. If you need advanced features like automatic retries or complex request interception, consider a more full-featured library. This is for when you want native fetch behavior with dramatically less boilerplate.

✅ Perfect for:

  • Removing boilerplate from fetch calls
  • Projects using native fetch today
  • Composable API clients
  • Simple use-cases where size matters

❌ Consider alternatives for:

  • Automatic retries or timeout handling
  • GraphQL (use a GraphQL client)
  • Complex request/response middleware
  • Very advanced edge-cases

Next Steps


Built with ❤️ by Kevin Whitley and the Itty community.