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

@hyperstackjs/funfetch

v1.3.0

Published

A type-safe, fetch based, functional HTTP toolkit for building API clients quickly.

Downloads

9

Readme

funfetch

A type-safe, fetch based, functional HTTP toolkit for building API clients quickly.

Features

  • 🌍 Universal: works on browsers and node.js
  • 🍰 Functional: use the whole thing, or pieces of it -- build your own mix
  • 🦸‍♀️ Developer first: better developer experience for 3rd party APIs -- draw your routes, get your client
  • 🦺 Type safe: Built type safe with Typescript
  • 🍭 Use case driven: and not REST driven -- funfetch is designed to support many use cases and not just one paradigm
  • 👟 Make simple things easy and run fast automatic JSON parsing, good defaults, great debugging story
  • 🔌 Extensible: use your own response handler and error handler
  • 👍 Lightweight: funfetch is actually a microframework, with a minimal set of dependencies
  • 🔑 BYOF: bring your own fetch. Use the fetch abstraction you prefer -- we use a universal fetch implementation by default

Quick Start

$ pnpm add @hyperstackjs/funfetch

A quick network action:

import { funfetch } from '@hyperstackjs/funfetch'
const { get } = funfetch()

// this function now represents the network call, can be reused, passed around
const callHome = get('/home')

// make the request
await callHome()

You can pass your network actions to a higher abstraction (a class) for better architecture and/or testing posture:

import { funfetch } from '@hyperstackjs/funfetch'
const { get } = funfetch()
const httpResultFetcher = get("/results")

class Superclient{
    cache: null
    constructor(private fetchResults)
    async getResults(){
        if(!cache){
            const res = await this.fetchResults()
            this.cache = res
        }
    }
}
const client = new Superclient(httpResultFetcher)

Or just quickly shape your client using a dict like you want it:

const createClient = ()=>{
    const {get, post} = funfetch()
    return {
        users: {
            create: post(({userId})=>`/users/${userId}`),
            list: get('/users')
        } 
    }
}

const client = createClient()
const res = await client.users.create({
    params: {userId: 'foobar'}, 
    data: {firstName: "Foo", lastName: "Bar"}
})
const res = await client.users.list()

Running Examples in examples/

All of our examples are runnable, and you can go to /examples to try them out.

Some examples on the CLI (demos universal usage on node):

$ pnpm ts-node examples/jwt.ts
fetching...
ok {
  body: {
    args: {},
    headers: {
...
$ pnpm ts-node examples/parameters-and-data.ts
$ pnpm ts-node examples/headers.ts
$ pnpm ts-node examples/basic-auth.ts

For a React example (demo universal usage on the browser):

Full vs Simple Response

By default, responses are just the body, JSON or otherwise are detected automatically. If you want a full response (status, body, headers), you can turn on a flag:

const { get } = funfetch({
  fullResponse: true,
})

Parameters, Data and Query

  • params - URL / route parameters. Pass a function to any funfetch request creator and you'll get those as the function parameter (see { id } below)
  • data - will be POSTed
  • query - a dictionary with key/value pairs that will turn into a properly escaped query string
const ok = get(({ id }) => `https://postman-echo.com/get/?id=${id}`)
const send = post(`https://postman-echo.com/post`)

console.log(
'ok',
await ok({ params: { id: 1 }, query: { search: 'foobar' } })
)

// nicely ignores params because 'send' is not a function
console.log('ok', await send({ params: { id: 1 }, data: { hello: 'world' } }))

Cache, Throttling, Retry, and More

We are by-design deferring these to other libraries, to be use in composition with funfetch. A great example is react-query:

const repoData = get('https://api.github.com/repos/tannerlinsley/react-query')
const { isLoading, error, data } = useQuery('repoData', repoData)

Bearer Token / JWT Auth

Bearer and JWT auth are first-class and have a dedicated flag (bearer), set it to use:

const { get } = funfetch({
  fullResponse: true,
  bearer: 'any-token',
})

Basic Auth

const { get } = funfetch({
  fullResponse: true,
  basic: { user: 'postman', password: 'password' },
})

const ok = get('https://postman-echo.com/basic-auth')

Both bearer and basic have their dynamic counterparts getBasic and getBearer for cases where you need to fetch this dynamically from localstorage.

Custom Headers

You can set anything that a proper node Request takes (RequestInit type) as a baseline for all requests, between those, headers:

const { get } = funfetch({
  baseOpts: {
    headers: {
      'User-Agent': 'foobar'
    }
  },
})

Error Handling

You can check for status code, and content:

const { get } = funfetch({
  fullResponse: true,
  throwIfBadStatus: (res) => {
    if (res.status === 404) {
      throw new Error('Hello from custom error')
    }
  },
  throwIfError: (res) => {
    if (res.body.match && res.body.match(/iana/)) {
      throw new Error('Hello from body matching error')
    }
  },
})

Debugging

For node:

$ DEBUG=funfetch yarn ts-node examples/debugging.ts

On browsers:

(console) localStorage.debug='funfetch'

And then refresh

const { get } = funfetch({
  fullResponse: true,
  throwIfBadStatus: (res) => {
    if (res.status === 404) {
      throw new Error('Hello from custom error')
    }
  },
  throwIfError: (res) => {
    if (res.body.match && res.body.match(/iana/)) {
      throw new Error('Hello from body matching error')
    }
  },
})

const ok = get('http://echo.jsontest.com/key/value/one/two')
const bad = get('http://github.com/404')
const lookForIana = get('http://example.com')

License

MIT