npm package: @personio/request
Description: A helper library used in Personio UI to perform HTTP requests. It also takes care to include the `X-CSRF-Token` on every request. Written in TypeScript, uses axios in the background.
Latest version: 2.1.0

# @personio/request

A helper library used in Personio UI to perform HTTP requests. It also takes care to include the `X-CSRF-Token` on every request. Written in TypeScript, uses axios in the background.

## Installation

Run

```bash
yarn add @personio/request
```

## Usage

In the root of your project, import `configureRequest` and execute it to provide a configuration to subsequent requests performed using the library:

```tsx
import { configureRequest } from '@personio/request';

configureRequest({ baseURL: API_BASE_URL, 
                   timeout: REQUEST_TIMEOUT,
                // Default is false. When enabled, returns the data content instead of an object with data as prop.
                // This is useful to avoid something like data.data when mapping the response. 
                   retrieveDataFromResponse: true,
                // Default is false, when enabled, dispatches the SESSION:EXPIRED event.
                // This makes the login modal from the monolith be triggered.  
                   dispatchSessionExpiredEvent: true,
                // Number in milliseconds, default is 0. Adds a delay to the session expired event.
                // Useful for finishing some animations like a closing a modal before it shows the login modal.
                   sessionExpiredDelay: 300 
                });
```

Perform an actual HTTP request anywhere in the code:

```tsx
import request from '@personio/request';
 
const response = await request({ method: "GET", url: 'example' }); 
```

Behind the scenes, `request` passes the arguments as is to axios. See more about possible options on https://github.com/axios/axios.


### With useRequest hook

It's possible to perform HTTP requests directly in React components or create custom React hooks using the `useRequest` hook provided by the library:

```tsx
import { useRequest, wasRequestSuccessful } from '@personio/request'; 
import { useCallback, useEffect } from "react";

type DataType = { id: number, value: string };
type MetaType = { example: string };

const useRequestExample = () => {  
  const [
      {
        data = [],
        statusCode,
        hasRequested,
        cancelSource,
        meta,
        isLoading,
        error,
      },
      makeRequest,
      { resetData, setHasNotRequested }
    ] = useRequest<DataType[], MetaType>();
  
  const fetch = useCallback((id: number) =>
    makeRequest(
      {
        method: 'GET',
        url: `example/${id}`,
      }      
    ), [makeRequest]);

  useEffect(() => {
    if (!statusCode) {
      return;
    }

    if (wasRequestSuccessful(statusCode)) {
      // do some action on success
    } else {
      // do some action on fail
    }
  }, [statusCode]);

  return [
   { data, meta, isLoading, error, statusCode, hasRequested, cancelSource },
   { fetch, resetData, setHasNotRequested }
  ] as const;
};

export default useRequestExample
```

The `makeRequest` function can also accept a second argument, an object that defines request options:

```javascript
{
  // prop to indicate if we should clear or persist the data between requests.
  // default: false
   persistData: boolean
}
```

## Testing

Use `createMock` and `mockResponse` 

```tsx
import request, { createMock, mockResponse, getRequestAmount } from '@personio/request';
import { render, screen } from '@testing-library/react';

type Response = {
  data: { id: number; text: string }[];
};

const mockAdapter = createMock(request);

const exampleMock = mockResponse({ method: 'GET', url: /example/ });

describe('<Example/>', () => {
  beforeEach(() => {
      exampleMock.reply<Response>(200, { data: [{id: 1, text: 'example'}] });
    });
  afterEach(() => {
      mockAdapter.reset();
  });
 
  it('should render with text "example"', async () => {    
      render(<Example />);
        
      expect(await screen.findByText('example')).toBeInTheDocument();      
  });
  
  it('should call the example API only once"', async () => {
      render(<Example />);
  
      await screen.findByText('example')

      expect(getRequestAmount(mockAdapter.history.get, /example/)).toBe(1);      
  });  
  
});
```

The `axios-mock-adapter` with a modified API is used to mock HTTP requests. For more info, check https://github.com/ctimmerm/axios-mock-adapter.