# haper

> Simple JS fetching library with 0 production dependencies

Latest version **1.2.0** (published 2021-01-13) · ISC license · 0 weekly downloads

## Install

```sh
npm install haper
pnpm add haper
yarn add haper
bun add haper
```

## Health

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

Positive: has types; no vulnerabilities.

Warnings: low downloads; no esm support.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 1.2.0 |
| Published | 2021-01-13 |
| First published | 2020-10-19 |
| Weekly downloads | 0 |
| License | ISC |
| TypeScript types | bundled |
| Module format | CommonJS |
| Dependencies | 0 |
| Unpacked size | 28.6 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Author | Dawid Wojda |
| Maintainers | dawiidio |
| Keywords | haper, fetch, js, fetching |

## Links

- npm: https://www.npmjs.com/package/haper
- npm.io page: https://npm.io/package/haper

## Alternatives

- [@mapbox/jsonlint-lines-primitives](https://npm.io/package/@mapbox/jsonlint-lines-primitives.md) — 5.3M weekly downloads
- [reftools](https://npm.io/package/reftools.md) — 3.5M weekly downloads
- [@hey-api/openapi-ts](https://npm.io/package/@hey-api/openapi-ts.md) — 3.5M weekly downloads
- [@mapbox/geojson-rewind](https://npm.io/package/@mapbox/geojson-rewind.md) — 2.4M weekly downloads
- [turbo-stream](https://npm.io/package/turbo-stream.md) — 1.7M weekly downloads

## Recent versions

- 1.2.0 (latest) — 2021-01-13
- 1.1.0 — 2020-12-21
- 1.0.4 — 2020-10-20
- 1.0.3 — 2020-10-20
- 1.0.2 — 2020-10-20
- 1.0.1 — 2020-10-19
- 1.0.0 — 2020-10-19

## README

# Haper
Haper is small library, just a syntax sugar with 0 dependencies for native window.fetch

## Install
```sh
npm install --save haper
```

## Usage
Quick example

```typescript
import { createHaper } from 'haper';

const haper = createHaper({
    baseUrl: 'http://localhost:3000'
});

interface User {
  name: string
  surname: string
  email: string
}

(async () => {
  const user = await haper.get<User>('/user');

  console.log(user.name, user.surname);
})();
```

#### Other methods
```typescript
import { createHaper } from 'haper';

const haper = createHaper({
    baseUrl: 'http://localhost:3000'
});

(async () => {
    await haper.get('/user/1');
    await haper.post('/user', {
        name: 'Dawid',
        surname: 'Wojda',
        email: 'dawid@test.com'
    });
    await haper.delete('/user/1');
    await haper.put('/user/1', {
        name: 'David',
    });
    await haper.patch('/user/1', {
        name: 'David',
    });
})();
```

### Api builder approach
Haper exports function `createApiBuilder` which enables different approach for defining 
API endpoints. That approach allows you to create endpoints with corresponding models,
parameters and faker functions in one place. But code example express more than words so 
here we go: 

```typescript jsx
import {
    createHaper,
    createApiBuilder,
} from 'haper';

export const haper = createHaper({
    baseUrl: 'http://localhost:3000/api'
});

const apiBuilder = createApiBuilder(haper, {
    faker: false, // if true haper will call fake functions (if defined) to get the data instead of talking to server
});

// very simple example of params object (DTO object)
interface AddressDto {
  city: string;
  street: string;
}

// also very simple interface of model returned from endpoints
interface Address {
  id: number;
  city: string;
  country: string;
}

export const getAddressList = apiBuilder
    .get<Array<Address>, {}>('address')
    .fake(() => {
        return [
            {
                id: 1,
                city: 'Warsaw',
                country: 'Poland'
            },
            {
                id: 1,
                city: 'Vilnius',
                country: 'Lithuania'
            },
        ];
    });

// here we have an important example of defining url variables
// Endpoint url is interpolated with values passed in params object
// then matched values will be deleted from params object before send to server
// to match base dto object interface
export const updateAddress = apiBuilder
    .put<Address, AddressDto & { userId: number }>('user/:userId/address')
    .fake(() => {
        return {
            id: 1,
            city: 'Warsaw',
            country: 'Poland'
        };
    });

// usage of defined endpoints
async function main() {
    const address = await updateAddress({
        userId: 8, // userId will match url param so it will be deleted and server receives only city and country keys 
        city: 'Prague',
        country: 'Czech Republic'
    });
    
    console.log(address);
}
```

#### canceling request
Haper methods returns promises with additional method `cancel()`
which comes useful in SPA applications when your component doesn't 
need data it's asked for and simply can cancel request before resolve

```typescript
import { createHaper } from 'haper';

const haper = createHaper({
    baseUrl: 'http://localhost:3000'
});

interface User {
  name: string
  surname: string
  email: string
}

(async () => {
  const promise = haper.get<User>('/user');

  promise
    .then((user) => {
        console.log(user.name, user.surname);
    })
    .catch(reason => {
        if (reason === 'cancel') {
            console.log('Request canceled');
        }
    })

  promise.cancel();
})();
```

#### Interceptors
There are a few cases when you may need interceptors, for example:
- add `Authorization` header to every Request
- search for `error` key in backend response
- and probably a few more

interceptor in Haper works as pure functions which may process request or response and is
registered via two methods available in Haper instance:
- `registerRequestInterceptor` - process request
- `registerResponseDataInterceptor` - process response data

Interceptors have very simple filters system, for example if you want to intercept every
data from all responses you may do something like this:

```typescript
import { createHaper } from 'haper';

const haper = createHaper({
    baseUrl: 'http://localhost:3000'
});

haper.registerResponseDataInterceptor('* * *', (data) => {
    console.log(data);

    return data;
});
```

Three asterisks in method call `* * *` are filters. Asterisks depends on number means:
1. method - supported values `'POST' | 'GET' | 'PUT' | 'PATCH' | 'DELETE' | '*'`
2. url - absolute url for filtered request. If baseUrl were specified on instance then all
urls will be relative to it
3. data type - response or request data type, for now supported values are: `json` for requests
and `'arrayBuffer' | 'blob' | 'formData' | 'json' | 'text'` for responses 

Interceptors works in "pipe" mode - every interceptor matched for request or response 
gets output data from previous to its input, so final data might be composed from many
chained interceptors

Interceptor url may contains interpolation placeholders which starts with ":", for example:
`/user/:id/profile` will be matched with `/user/1/profile`

Examples:

1. Add authorization header to all `POST` requests
```typescript
import { createHaper } from 'haper';

const haper = createHaper({
    baseUrl: 'http://localhost:3000'
});

const secretToken = 'XcdffrYUfd334ffsdf09sMdj2';

haper.registerRequestInterceptor('POST * *', request => {
    request.headers.append('Authorization', `Bearer ${secretToken}`);
});
```

2. Search for `error` key in data returned from all `/user` endpoints and throw error if present
```typescript
import { createHaper } from 'haper';

const haper = createHaper({
    baseUrl: 'http://localhost:3000'
});

interface RestData {
  data: any,
  error?: string
}

haper.registerResponseDataInterceptor('* /user *', (data:RestData) => {
    if (data.error) {
      throw new Error(`Error from server: ${data.error}`);
    }
    
    return data.data;
});
```

## Roadmap
- [x] Faker functions
- [ ] Support for other data types than simple JSON (eg. text, formData)
- [ ] Support for file uploading
  - [ ] progress
  - [ ] keep promise-like api
- [ ] Support for file downloading (?)
- [ ] Improve filters system
- [ ] In memory(?) cache system
- [ ] Rethink interceptors concept, especially with types in mind

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