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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@alduino/api-utils

v0.5.0

Published

A collection of utilities to build an API client

Readme

API Utilities

This library contains a collection of utilities to build an API client.

Tutorial

This library is designed to be used by machine-generated code (i.e. swagger-codegen). It is probably going to be very tedious to write a library using it by hand.

Re-exports

First off, there are some things you should re-export from the library, as they are meant for use by your library's user.

  • useSwr, useSwrInfinite, useFetch, useFetchDeferred, sendRequest, getEndpointUrl, useEndpointUrl: These hooks and functions are the core of how your API will be used. If you use wrapper functions, you don't need to export these.
  • SWRConfiguration, SWRResponse, SWRInfiniteConfiguration, SWRInfiniteResponse, UseAsyncCallbackOptions, UseAsyncReturn: Theses are various types used by useSwr and useFetch (re-exported from the swr and react-async-hook libraries)

Usage

The library revolves around Endpoints. These are how your library maps some request data to a URL to send the actual request to.

An endpoint implementation would look something like:

import {Endpoint, key} from "@alduino/api-utils";

export interface Request {
    userId: string;
    size: number;
    body: string;
}

export interface Response {
    url: string;
}

export const userAvatarEndpoint: Endpoint<Request, Response> = {
    apiContext,
    getKey({userId, size}: Request) {
        return key`user/${userId}/avatar?${{size}}`;
    },
    fetch(url: string, body: string): Promise<Response> {
        return fetch(url, {
            credentials: "include",
            body: JSON.stringify(body)
        }).then(res => res.json());
    }
};

You can generate the Request interface by merging the URL parameters and the query parameters, then split them back out again via object destructuring in getKey().

API Context

Notice the apiContext key in the endpoint above. This is an "API Context", which is what api-utils uses to map your keys to a full URL, as well as a way for a user to specify configuration for swr. The API context is based off of React Contexts.

In a file that can be imported by any endpoint files, add this code (it can be a static file, no generation is needed):

import {createContext} from "@alduino/api-utils";

export const apiContext = createContext();
export const ApiProvider = context.Provider;

The apiContext export should then be used as the apiContext value you saw in the endpoint example above.

You need to export the ApiProvider to users of your library, as it is how they specify required configuration like the base URL.

Library API

There are two forms your library's API can take: wrapper functions (recommended), or exposed endpoints. Wrapper functions are recommended because they provide a better DX.

Wrapper functions

This form is recommended for better DX and API safety - each endpoint has its own hook, and it is not possible to useInfinite where it doesn't make sense.

With this form, you generate functions that wrap useSwr, useFetch, and useFetchDeferred, and provide the endpoint to it automatically. The generated code might look something like:

import {useSwr, useFetch, useFetchDeferred, sendRequest, SWRConfiguration, SWRResponse} from "@alduino/api-utils";

export function useUserAvatar(
    request: Request,
    config?: SWRConfiguration
): SWRResponse<Response, Error> {
    return useSwr(userAvatarEndpoint, request, config, "useUserAvatar");
}

export function useUserAvatarFetch(
    request: Request,
    config?: UseAsyncCallbackOptions<Response>
): UseAsyncReturn<Response | null, []> {
    return useFetch(userAvatarEndpoint, request, config, "useUserAvatarFetch");
}

export function useUserAvatarFetchDeferred(
    config?: UseAsyncCallbackOptions<Response>
): UseAsyncReturn<Response | null, [Request | null]> {
    return useFetchDeferred(
        userAvatarEndpoint,
        config,
        "useUserAvatarFetchDeferred"
    );
}

export function sendUserAvatarRequest(baseUrl: string, request: Request): Promise<Response> {
    return sendRequest(userAvatarEndpoint, baseUrl, request);
}

export function getUserAvatarEndpointUrl(baseUrl: string, request: Omit<Request, "body">): string {
    return getEndpointUrl(userAvatarEndpoint, baseUrl, request);
}

export function useUserAvatarEndpointUrl(request: Omit<Request, "body">): string {
    return useEndpointUrl(userAvatarEndpoint, request, "useUserAvatarEndpointUrl");
}

Note the last parameter passed, this value should be the same as the name of your wrapper function, to provide better error messages.

This can then be consumed as:

import {useUserAvatar} from "my-api-client";

// declarative mode: swr hook
const {data, error} = useUserAvatar({userId: "bob", size: 256});

// imperative mode: react-async-hook
const {result, error, execute} = useUserAvatarFetch({userId: "bob", size: 256});
execute();

You should also generate useSwrInfinite versions where relevant.

Exposed endpoints

If you do not want to generate wrapper functions, you can export your Endpoint values as well as the useSwr, useSwrInfinite, useFetch, and useFetchDeferred functions provided by this library. A user of your library can then call useSwr directly:

import {useSwr, userAvatarEndpoint} from "my-api-client";

const {data, error} = useSwr(userAvatarEndpoint, {userId: "bob", size: 256});

API

Endpoint<Request, Response>

Tells the library how and where to send a request. An Endpoint has three keys:

apiContext

This is the React context returned from createContext() that specifies information like the base URL and default request options.

getKey(request: Request)

This function should convert the request into a URL, relative to the base URL (i.e. should not start with a /).

It is recommended that you use the key function for this, it makes it much neater. It’s also easy to generate code for.

fetch(url: string, [body: Body])

This function should send the actual request to the URL provided. The URL is created from the base URL passed to the ApiProvider as well as the return value of this endpoint’s getKey function.

Any other request options (method, cookies etc) should be hardcoded in the call here.

Body parameter

If a request requires a body parameter, set it as a field called body in the Request type. If this field exists, its value will be passed to this function as a second parameter.