# @alduino/api-utils

> A collection of utilities to build an API client

Latest version **0.5.0** (published 2022-01-30) · MIT license · 0 weekly downloads

## Install

```sh
npm install @alduino/api-utils
pnpm add @alduino/api-utils
yarn add @alduino/api-utils
bun add @alduino/api-utils
```

## Health

**Score 30/100 (F)** — status: abandoned.

Positive: has types; esm support; no vulnerabilities; high quality score.

Warnings: low downloads; pre 1.0.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 0.5.0 |
| Published | 2022-01-30 |
| First published | 2021-08-28 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Dependencies | 4 |
| Unpacked size | 50.1 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 0 |
| Maintainers | alduino |
| Keywords | api, client, utilities, code-generation |

## Links

- npm: https://www.npmjs.com/package/@alduino/api-utils
- Repository: https://github.com/Alduino/api-utils
- Homepage: https://github.com/Alduino/api-utils#readme
- Issues: https://github.com/Alduino/api-utils/issues
- npm.io page: https://npm.io/package/@alduino/api-utils

## Dependencies (4)

- [swr](https://npm.io/package/swr.md) ^1.2.0
- [tslib](https://npm.io/package/tslib.md) ^2.3.1
- [tiny-invariant](https://npm.io/package/tiny-invariant.md) ^1.1.0
- [react-async-hook](https://npm.io/package/react-async-hook.md) ^3.6.2

## Alternatives

- [launchdarkly-js-client-sdk](https://npm.io/package/launchdarkly-js-client-sdk.md) — 2.5M weekly downloads
- [@elastic/elasticsearch](https://npm.io/package/@elastic/elasticsearch.md) — 2.1M weekly downloads
- [@c8y/client](https://npm.io/package/@c8y/client.md) — 15.3K weekly downloads
- [@signaldb/maverickjs](https://npm.io/package/@signaldb/maverickjs.md) — 1.7K weekly downloads
- [@bbc/http-transport-cache](https://npm.io/package/@bbc/http-transport-cache.md) — 1.2K weekly downloads

## Recent versions

- 0.5.0 (latest) — 2022-01-30
- 0.4.3 — 2022-01-27
- 0.4.2 — 2022-01-01
- 0.4.1 — 2021-12-27
- 0.4.0 — 2021-10-04
- 0.3.0 — 2021-09-04
- 0.2.1 — 2021-08-30
- 0.2.0 — 2021-08-30
- 0.1.0 — 2021-08-28

## 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](#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 `Endpoint`s. 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:

```typescript
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):

```typescript
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:

```typescript
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:

```typescript
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:

```typescript
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.

---
_Source: https://npm.io/package/@alduino/api-utils · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
