# useQuery

- [Introduction](#introduction)
- [Installation](#installation)
- [useQuery](#usequery-1)
  - [Parameters](#parameters)
  - [Returned parameters](#returned-parameters)
- [generateApiClient](#generateapiclient)
  - [Parameters](#parameters-1)
- [generateJwtApiClient](#generatejwtapiclient)
  - [Parameters](#parameters-2)
- [Examples](#examples)
  - [Example 1](#example-1)
  - [Example 2](#example-2)
  - [Example with JWT](#examplewithjwt)

## 1 - Introduction

This hook can be used to perform queries to an endpoint. Allows easy management of operations and query status.

It requires an Axios client. This library already provides the `generateApiClient` function which returns a client with a variable base url and an interceptor to send an authentication header. You may also create an axios client by your own.

## 2 - Installation

Install the library with `npm install @hybris-software/use-query`.

At the upper level of the application you should also insert the `ApiProvider` component which requires an `apiClient` prop. It should be an Axios client which you can create by your own or use `generateApiClient` to generate one.

Here is an example:

```javascript
import { generateApiClient, ApiProvider } from "@hybris-software/use-query";
...
const apiClient = generateApiClient({
  baseUrl: "https://my.api.com/api/v1",
  authorizationHeader: "Authorization",
  authorizationPrefix: "Bearer "
})
...
root.render(
  <React.StrictMode>
    <ApiProvider apiClient={apiClient}>
      <App />
    </ApiProvider>
  </React.StrictMode >
);
```

## 3 - generateApiClient

This function returns an Axios client with the possibility to set a `baseUrl`, and an authorization header. It gets the authorization token from `localStorage` or `sessionStorage` with the key `token`.

### 3.1 - Parameters

| Parameter           | Type   | Default         | Description                                                                                       |
| ------------------- | ------ | --------------- | ------------------------------------------------------------------------------------------------- |
| baseUrl             | string |                 | Axios base url                                                                                    |
| timeout             | number | 5000            | Default timeout (milliseconds)                                                                    |
| authorizationHeader | string | "Authorization" | Authorization header name                                                                         |
| authorizationPrefix | string | "Bearer "       | The authorization header prefix, for example `Authorization: Bearer your_token_from_localstorage` |
| localStorageKey     | string | "token"         | Local storage key that contains the authentication token                                          |

### 3.2 - Our api client

This is what `generateApiClient` generates, you can use it as a base to create your own api client.

```javascript
const apiClient = axios.create({
  baseURL: baseUrl,
  timeout: 5000,
  headers: {
    "Content-Type": "application/json",
    Accept: "application/json",
  },
});

apiClient.interceptors.request.use(
  function (config) {
    config.headers = config.headers || {};
    const token =
      localStorage.getItem("token") || sessionStorage.getItem("token");
    if (token) {
      config.headers[authorizationHeader] = `${authorizationPrefix}${token}`;
    }
    config.headers["Content-Type"] = "application/json";
    config.headers["Accept"] = "application/json";
    return config;
  },
  function (error) {
    return Promise.reject(error);
  }
);

apiClient.interceptors.response.use(
  function (response) {
    return response;
  },
  function (error) {
    return Promise.reject(error);
  }
);
```

## 4 - generateJwtApiClient

This function returns an Axios client with the possibility to set a `baseUrl`, and an authorization header. It gets the authorization token from `localStorage` or `sessionStorage` with the key `token`.

### 4.1 - Parameters

| Parameter                   | Type     | Default         | Description                                                                                                                |
| --------------------------- | -------- | --------------- | -------------------------------------------------------------------------------------------------------------------------- |
| baseUrl                     | string   |                 | Axios base url                                                                                                             |
| timeout                     | number   | 5000            | Default timeout (milliseconds)                                                                                             |
| authorizationHeader         | string   | "Authorization" | Authorization header name                                                                                                  |
| authorizationPrefix         | string   | "Bearer "       | The authorization header prefix, for example `Authorization: Bearer your_token_from_localstorage`                          |
| accessTokenLocalStorageKey  | string   | "accessToken"   | Local storage key that contains the access token                                                                           |
| refreshTokenLocalStorageKey | string   | "refreshToken"  | Local storage key that contains the refresh token                                                                          |
| refreshTokenFunction        | function |                 | An asyncronous function that receives the old access token and refresh token and returns [newAccessToken, newRefreshToken] |

## 5 - useQuery

This hook performs only one query, if you have to perform multiple queries in parallel you can call multiple times `useQuery` or use `useMultipleQueries` as described below.

### 5.1 - Parameters

| Parameter          | Type                 | Default     | Description                                                                                                                                                                                                                                                                                |
| ------------------ | -------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| url                | string               |             | Endpoint url                                                                                                                                                                                                                                                                               |
| method             | string               | 'GET'       | Request method (GET, POST...)                                                                                                                                                                                                                                                              |
| executeImmediately | boolean              | false       | Sets whether the call should be executed when the component is created or wait for the call to `executeQuery()`                                                                                                                                                                            |
| onSuccess          | `(response) => void` | `() => { }` | Function executed after a successful query                                                                                                                                                                                                                                                 |
| onUnauthorized     | `(error) => void`    | undefined   | Function executed after an unsuccessful query if the response code is 401 (optional, see `onError`). The default function is the one defined in the `ApiProvider` if it is not specified in useQuery. To disable the default one and not use an `onUnauthorized` set `onUnauthorized=null` |
| onError            | `(error) => void`    | `() => { }` | Function executed after an unsuccessful query. If `onUnauthorized` is not defined, it also handles 401 status code                                                                                                                                                                         |
| clientOptions      | object               | `{}`        | Extra Axios options, ex. `{timeout: 1000}`                                                                                                                                                                                                                                                 |

### 5.2 - Returned parameters

| Parameter    | Type                  | Description                                                                                                                                    |
| ------------ | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| isLoading    | boolean               | `true` while the query is being executed, `false` otherwise, even if it has not yet started                                                    |
| isError      | boolean               | `true` while the query finished unsuccessfully, `false` otherwise                                                                              |
| isSuccess    | boolean               | `true` while the query finished successfully, `false` otherwise                                                                                |
| response     | any                   | The query response if it finished successfully, `undefined` otherwise                                                                          |
| error        | any                   | The generated error if the query finished unsuccessfully, `undefined` otherwise. If it got a response, it can be accessed via `error.response` |
| executeQuery | `(data?: {}) => void` | Trigger the query with optional body as parameter                                                                                              |

## 6 - useMultipleQueries

This hooks allows to perform multiple queries in parallel with a syntax similar to that of `useQuery`.

### 6.1 - Parameters

| Parameter          | Type                 | Default     | Description                                                                                                                                                                                                                                                                                          |
| ------------------ | -------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| queries            | object               |             | Object where the key is the name of the query. The content variables are described below.                                                                                                                                                                                                            |
| -- url             | string               |             | Endpoint url                                                                                                                                                                                                                                                                                         |
| -- method          | string               | 'GET'       | Request method (GET, POST...)                                                                                                                                                                                                                                                                        |
| -- data            | object               | { }         | Request body                                                                                                                                                                                                                                                                                         |
| -- onSuccess       | `(response) => void` |             | Function executed after a successful query                                                                                                                                                                                                                                                           |
| -- onUnauthorized  | `(error) => void`    |             | Function executed after an unsuccessful query if the response code is 401 (optional, see `onError`). The default function is the one defined in the `ApiProvider` if it is not specified in useMultipleQueries. To disable the default one and not use an `onUnauthorized` set `onUnauthorized=null` |
| -- onError         | `(error) => void`    |             | Function executed after an unsuccessful query. If `onUnauthorized` is not defined, it also handles 401 status code                                                                                                                                                                                   |
| executeImmediately | boolean              | false       | Sets whether the call should be executed when the component is created or wait for the call to `executeQueries()`                                                                                                                                                                                    |
| onEnd              | `(response) => void` | `() => { }` | Function executed after after all the queries finished                                                                                                                                                                                                                                               |
| clientOptions      | object               | `{}`        | Extra Axios options, ex. `{timeout: 1000}`                                                                                                                                                                                                                                                           |

### 6.2 - Returned parameters

| Parameter      | Type                  | Description                                                                                                                                                 |
| -------------- | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| executeQueries | `(data?: {}) => void` | Start the queries with optional body as parameter. `data` should be an object where the key is the name of the query and the value is the actual query data |
| errors         | object                | Object containing all the received errors. The key is the name of the query, the value is the error                                                         |
| responses      | object                | Object containing all the received successful responses. The key is the name of the query, the value is the response                                        |
| statuses       | object                | Object containing the status of each query. The key is the name of the query, the value is the status                                                       |
| isLoading      | boolean               | `true` if any calls are in progress, `false` otherwise, even if it has not yet started                                                                      |
| queries        | object                | Contains all the information of each query. The key is the name of the query, the value is an object with the following queries: `status, error, response`  |

## 7 - Examples

### 7.1 - Example 1

```javascript
const { isLoading, executeQuery } = useQuery({
  url: "accounts/login/", // If baseUrl has been set, you can use a relative url. It also accepts absolute urls.
  method: "POST",
  executeImmediately: false,
  onSuccess: (response) => {
    console.log(response);
  },
  onUnauthorized: (response) => {
    console.log(response);
  },
});

const submitForm(value) => {
  executeQuery(value);
}

if(isLoading)
  return <Loader />
else
  return <Form submitForm={submitForm} />
```

### 7.2 - Example 2

```javascript
const { isLoading, isSuccess, data, error } = useQuery({
  url: "api/v1/userinfo/",
  method: "GET",
  executeImmediately: true,
});

if (isLoading) return <Loader />;
else if (isSuccess) return <UserInfo data={data} />;
else return <Error error={error} />;
```

### 6.3 - Example with useMultipleQueries

```javascript
const { queries } = useMultipleQueries({
  queries: {
    query1: {
      url: "https://jsonplaceholder.typicode.com/todos/1",
      onSuccess: (response) => {
        console.log("query1", response);
      },
    },
    query2: {
      url: "https://jsonplaceholder.typicode.com/todos/2",
      onError: (error) => {
        console.log("query2 error", error);
      },
      onSuccess: (response) => {
        console.log("query2 success", response);
      },
    },
    query3: {
      url: "https://wrongdomain/todos/3",
      onError: (error) => {
        console.log("query3", error);
      },
    },
  },
  executeImmediately: true,
  onEnd: () => {
    console.log("All done");
  },
});
```

### 7.3 - Example with JWT

```javascript
import axios from "axios";
import { generateJwtApiClient, ApiProvider } from "@hybris-software/use-query";
...
const apiClient = generateJwtApiClient({
  baseUrl: "https://my.api.com/api/v1",
  authorizationHeader: "Authorization",
  authorizationPrefix: "Bearer ",
  refreshTokenFunction: async ({accessToken, refreshToken}) => {
    const response = await axios.post("https://example.com/refresh", {accessToken, refreshToken})
    const updatedAccessToken = response.data.accessToken;
    const updatedRefreshToken = response.data.refreshToken;
    return [updatedAccessToken, updatedRefreshToken];
  }
})
...
root.render(
  <React.StrictMode>
    <ApiProvider apiClient={apiClient} onUnauthorized={(response) => {console.log(response)}}>
      <App />
    </ApiProvider>
  </React.StrictMode >
);
```
