npm package: @ltipton/useapi
Description: React Hooks library for calling APIs
Author: Lance Tipton
Latest version: 0.2.1

# Build Client API

- Generates hooks to make API calls based on a passed in Api Options
- Allows defining a simple model for a group of endpoints to generate the hooks

## Api Options

- The Options are split into two parts `queries` and `mutations`
- With each, a `route-model` should be defined for each individual endpoint

### Query Endpoints

- All query endpoints should be defined under `queries` property
- All endpoints defined within the `queries` property will make a `get` request
  - The `get` request is **hard coded**, and currently can not be changed

### Mutation Endpoints

- All mutation endpoints should be defined under `mutations` property
- Mutation endpoints can make request of the following types
  - `post`, `patch`, `put`, `delete`
- Other than the `delete` methods, each mutation hook accepts a `payload` object
  - The `payload` object should include the body content of the request

### Route model

- Both `queries` and `mutations` are defined by a `route-model`
- A `route-model` is defined by the following properties
  - `method` - The HTTP verb to use when making the request
  - `route` - The request url path name, i.e. `/strategies/23/pillars`
    - Can be a string or function
    - As a function, it will be called with the `Hook Options` passed to the calling hook
  - `payload` - Used as the body of the request for `mutations` **only**
  - `onError` - Callback called when an error is thrown durning the request
  - `onSuccess` - Callback called when a request is successful
  - `onBefore` - Callback called before a request is made
    - Useful for validating the payload of a mutation
    - If this callback throws an error, it will be caught
    - All caught errors will skip the request and be passed to the `onError` callback as a `NetworkError`
    - If this callback returns `false`, a generic error will be throw
    - The callback should return anything other than `false` to not throw an error
  - `onUnMount` - Callback called when a `get` request is made and the calling Component unmounts
    - Useful for cleaning up local state when desired

### Api Options Example

```ts
const endpointHooks = buildApiClient({
  queries: {
    getAll: {
      // Will use 'get' regardless of what is defined here
      method: `get`,
      route: `/api/endpoint/pathname`,
      // Called when the parent component is unmounted
      onUnMount: () => {},
      // Called when a error is thrown durning a request
      // This includes errors in the `onBefore` callback
      onError: (error: any) => {},
      // Called when a request is successful
      onSuccess: <T>(result: T) => {},
    },
    get: {
      method: `get`,
      // The route as a function
      route: (data: HookMethodOptions) => `/endpoint/${data.payload.id}`,
      // Callback methods are not required
    },
  },
  mutations: {
    create: {
      // Can be any mutation HTTP verb - post, put, patch, delete
      method: 'post',
      // Accepts all the same arguments as queries
      // The `onUnMount` callback is ignored for all mutations
    },
  },
})
```

## Api Client Hooks

- The `buildApiClient` method returns an object with properties matching the passed in `Api Options` object
- It is split into two parts `queries` and `mutations` same as the `Api Options` object
- Within both the `queries` and `mutations` properties are **React Hook** functions
  - These are real hooks and follow all the same rules as normal react hooks
  - They must be called within the body of a `Function Component`
  - They can **NOT** be called conditionally
- These hooks use [closures](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures)
  - This allows them to maintain access to the `Api Options` object originally passed when they were being built
  - This allows defining properties and logic for an API request in a single location, and then reusing it where needed
- It is recommended to export the hooks built from the `buildApiClient` method using the `use` prefix
  - This is the same pattern used by react, let others know it is a hook, and not regular function
  - For example:
    - If I have a hook called `getItem`
    - I would export it as `useItem`

### Hooks Example

- Given the same `endpointHooks` as defined in the above example
- The `buildApiClient` method would return the following object

```ts
const endpointHooks = {
  queries: {
    // Contains references to the queries.get route-model above
    get: useQuery,
    // Contains references to the queries.getAll route-model above
    getAll: useQuery,
  }
  mutations: {
    // Contains references to the mutations.create route-model above
    create: useMutation,
  }
}
```

## Query Hooks

- The `useQuery` hook is built internally when the `buildApiClient` method is called
- All `route-model` defined in the `queries` property of the `Api Options` will be converted into a `useQuery` hook
- These hooks can be access from the `queries` property of the response of the `buildApiClient` method call
  - For example:
    - Given I pass the following `Api Options` object => `{ queries: { get: { ...<route-model> }}}`
    - I can access the hook for the `get` hook in the response from `buildApiClient` at `response.queries.get`
- Unlike the `mutation` hooks, the request made by the `useQuery` hook are initiated immediately

### Query Hook Options

- This hook accepts an `options` object with the similar properties as in the initial `Api Options` object `route-model`
- Properties in this object will impact original `route-model` properties in the following ways
  - **payload** => Is merged with the original **payload**
  - **onBefore** | **onSuccess** | **onError** | **onUnMount** => Will overwrite the original method
    - These methods can be defined inline without the need for memoization
    - The **onUnMount** callback exists for **mutations** only
  - **route** => When route is a function, the `options` object will be passed as the first argument
- Aside from the above properties, the `options` object has the following
  - **enabled** - If `false`, the API call will be by-passed
  - **cache** - If `true`, the response will be cached. Future calls will use the cached response until cleared
    - Cache exists for **queries** only
  - **apiOptions** - Object passed on to the axios client
    - **mapper?** - `(item: Record<string, any>[]) => any[]`
    - **config?** - [AxiosRequestConfig](https://www.npmjs.com/package/axios#request-config) from the axios library

### Query Hook Response

- The hook response contains the following properties
  - **loading** - Is `true` when the API request is in flight
  - **data** - The API client response. Is `undefined` until a successful API call is made
  - **error** - Network Error response. Is undefined until an error is thrown, or the API call fails
  - **clearCache** - Clears the cache for the **specific** API call
  - **getCache** - Returns the cached response for the **specific** API call

## Mutation Hooks

- The `useMutation` hook is built internally when the `buildApiClient` method is called
- All `route-model` defined in the `mutations` property of the `Api Options` will be converted into a `useMutation` hook
- These hooks can be access from the `mutations` property of the response of the `buildApiClient` method call
  - For example:
    - Given I pass the following `Api Options` object => `{ mutations: { post: { ...<route-model> }}}`
    - I can access the hook for the `post` hook in the response from `buildApiClient` at `response.mutations.post`
- Unlike the `query` hooks, the request made by the `mutation` hook are **NOT** immediately initiated
  - Instead they return a `useMutation` hook that must be called to initiate the API call

### Mutation Hook Options

- This hook accepts an `options` object with the similar properties as in the initial `Api Options` object `route-model`
- Properties in this object will impact original `route-model` properties in the following ways
  - **payload** => Is merged with the original **payload**
  - **onBefore** | **onSuccess** | **onError** | **onUnMount** => Will overwrite the original method
    - These methods can be defined inline without the need for memoization
    - The **onUnMount** callback exists for **mutations** only
  - **route** => When route is a function, the `options` object will be passed as the first argument
- Aside from the above properties, the `options` object has the following
  - **enabled** - If `false`, the API call will be by-passed
  - **apiOptions** - Object passed on to the axios client
    - **mapper?** - `(item: Record<string, any>[]) => any[]`
    - **config?** - [AxiosRequestConfig](https://www.npmjs.com/package/axios#request-config) from the axios library

### Mutation Hook Response

- The hook response contains the following properties
  - **loading** - Is `true` when the API request is in flight
  - **data** - The API client response. Is `undefined` until a successful API call is made
  - **error** - Network Error response. Is undefined until an error is thrown, or the API call fails
  - **useMutate** - Hook that will make the actual API request
    - Mutation hooks are not called immediately in the way that query hooks are
    - Instead this `useMutation` hook is returned and that must be called to initiate the API call