npm package: @studiokeywi/papi
Description: PAPI - The Proxy API tool to make API calls friendlier
Author: studioKeywi
Latest version: 0.1.7

# PAPI

> The Proxy API tool from studioKeywi for better DX. Zero dependencies. Built with ❤️ and [Bun](https://bun.sh/), [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/), [TypeDoc](https://typedoc.org/), and [Vitest](https://vitest.dev/)

![NPM Version](https://img.shields.io/npm/v/@studiokeywi/papi)
![NPM Type Definitions](https://img.shields.io/npm/types/@studiokeywi/papi)
![npm bundle size (scoped)](https://img.shields.io/bundlephobia/minzip/@studiokeywi/papi)
![NPM Unpacked Size](https://img.shields.io/npm/unpacked-size/@studiokeywi/papi)
![NPM License](https://img.shields.io/npm/l/@studiokeywi/papi)

## Installation

PAPI is available to install through the NPM registry and can be installed by any runtime that is compatible with NPM installs:

### Node

Node projects can install PAPI as a dependency:

```sh
npm install @studiokeywi/papi
```

### Bun

Bun projects can install PAPI as a dependency:

```sh
bun add @studiokeywi/papi
```

### Deno

Deno projects can import PAPI using the `npm:` specifier:

```typescript
import { papi } from 'npm:@studiokeywi/papi';
```

Or by adding to the import map of `deno.json`:

```json
{
  "imports": {
    "papi": "npm:@studiokeywi/papi"
  }
}
```

```typescript
import { papi } from 'papi';
```

> PAPI is a TypeScript first implementation. Along with providing and exporting its own types, exports are provided for the Bun and Deno runtimes as well as an explicit `@studiokeywi/papi/ts` export entry
>
> Under the hood, PAPI is a simple builder syntax combined with a custom [JS Proxy object][mdn proxy] used to wrap around the [Web API `fetch` method][mdn fetch]

> PAPI performs no validation on API responses, nor does PAPI provide any integrated security features. When building an API caller, PAPI allows an API key/token value to be provided automatically in the Authorization header in `Bearer: [token]` format in plaintext. This could be detected by any software that can read or intercept network requests
>
> Beyond this, PAPI allows any standard option that can be provided to the second argument of your runtime's implementation of the Web API `fetch` method. If you are concerned about the security/validity of your network calls, we strongly suggest utilizing other libraries in addition to/instead of PAPI to perform your security/validation

## Usage

PAPI is designed with a chainable builder approach to defining the shape of any API, combined with a chained property approach to calling the various API endpoints. The combination allows for a natural and expressive way to declare and request expected API responses

### PAPI in 4 lines

<!-- prettier-ignore -->
> ```typescript
> import { papi } from '@studiokeywi/papi';
>
> const url = 'https://jsonplaceholder.typicode.com';
> const albumShape = { userId: 0, id: 0, title: '' };
> const allAlbums = await papi(url)                     /* 1. Define a base URL */
>   .path('albums', albums => albums.get([albumShape])) /* 2. Define API endpoints */
>   .build()                                            /* 3. Create API caller */
>   .albums.get();                                      /* 4. Make an API request */
> // typeof allAlbums = { userId: number; id: number; title: string }[]
> ```

### API Builder

PAPI allows API endpoints to be defined as simply or robustly as needed. An expanded example of the JSONPlaceholder API's `/albums` endpoint is demonstrated below:

#### Example Endpoint

> ```typescript
> import { papi } from '@studiokeywi/papi';
>
> const albumShape = { userId: 0, id: 0, title: '' };
> const errShape = {};
> const photoShape = { albumId: 0, id: 0, title: '', url: '', thumbnailUrl: '' };
>
> const apiCaller = papi('https://jsonplaceholder.typicode.com')
>   .path('albums', $albums =>
>     $albums
>       .get([albumShape], $get => $get.error(errShape).queryOpt(albumShape))
>       .post(albumShape, $post => $post.bodyOpt(albumShape).error(errShape))
>       .slug($albumId =>
>         $albumId
>           .get(albumShape, $get => $get.error(errShape))
>           .delete({}, $del => $del.error(errShape))
>           .patch(albumShape, $patch => $patch.bodyOpt(albumShape).error(errShape))
>           .put(albumShape, $put => $put.bodyOpt(albumShape).error(errShape))
>           .path('photos', $photos => $photos.get([photoShape], $get => $get.error(errShape)))
>       )
>   )
>   .build();
> ```

See the [builder docs] for more

### API Caller

The API Caller tool allows arbitrary API endpoints to be called. Following from the example above:

#### Example Caller

> ```typescript
> const data = { title: "studioKeywi's Loudest Hits!" };
> const newAlbum = await apiCaller.albums.post({ data });
> if (!('id' in newAlbum)) throw new Error(`API Post failed and returned ${newAlbum}`);
>
> const photos = await apiCaller.albums[newAlbum.id].photos.get();
> if (!('length' in photos)) throw new Error(`API Get failed and returned ${photos}`);
>
> photos.forEach(photo => {
>   // ...
> });
> ```

See the [caller docs] for more

## API Documentation

Generated by TypeDoc: [API Documentation][api docs]

## Future

Think PAPI is missing a feature? [Open an issue!][git issue]

## Links

[PAPI Docs]  
[PAPI NPMJS]  
[PAPI Repo]

<!-- External Links -->

[api docs]: https://studiokeywi.github.io/papi/api-docs/
[builder docs]: https://studiokeywi.github.io/papi/builder/
[caller docs]: https://studiokeywi.github.io/papi/caller/
[git issue]: https://github.studiokeywi.dev/papi/issues/new
[mdn fetch]: https://developer.mozilla.org/en-US/docs/Web/API/fetch
[mdn proxy]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
[PAPI Docs]: https://studiokeywi.github.io/papi
[PAPI NPMJS]: https://npmjs.com/package/@studiokeywi/papi
[PAPI Repo]: https://github.studiokeywi.dev/papi