How to create an auth login system with axios interceptors (TypeScript) PART I
Tags: javascript, typescript, beginners, react
Published: 2022-04-26 16:37:29

Hi guys!

In this article I am sharing a small process of an auth system using axios interceptors. The concept below, is that, when you submit your email or password you want to check if that user exists on the server and if the user is authenticated then the user would be able to navigate to other pages. 

More information about interceptors you can check [here](https://blog.bitsrc.io/setting-up-axios-interceptors-for-all-http-calls-in-an-application-71bc2c636e4e). 

You could simply do a post request inside your SignIn component however if one day lets say axios doesn't exist anymore and you want to be able to change what you used in just one place but not the logic behind it then interceptors is the way to go. Also this is a concept recently came across called [MVP] - minimum viable product - [LINK] (https://medium.com/@harish_6956/minimum-viable-product-mvp-its-importance-pitfalls-f91ea3953d41), still trying to get my head around it but is good to know.

By the way the article is giving you an idea how that works of course you would need to add your own bits and pieces, api etc...

So let's take a step back.

What I am sharing below is:

- Setup the connection with a server.

- Create a service layer

### Set up with a server

1. Create your client, which is the connection with your API.

Below the ``REACT_APP_API_URL`` could be your own API saved on your .env file or .env.local

```js
interface ConfigType {
  ApiUrl: string
}

const config: ConfigType = {
  ApiUrl: process.env.REACT_APP_API_URL || '',
}

export default config
```
Here we have our ``http-client file.``

```js

import axios from 'axios'
import config from './config'
import setupInterceptorsTo from './http-client-interceptor'

const instance = axios.create({
  baseURL: config.ApiUrl,
  headers: {
    'Content-type': 'application/json',
  },
})

export default setupInterceptorsTo(instance)

```
### Create the service layer

Below we have the interceptors which will handle our error messages because there is always different type of errors.

```js

import { AxiosError, AxiosInstance, AxiosRequestConfig } from 'axios'
import { toast } from 'react-toastify'
import { isObject, isEmpty } from 'lodash'

const API_DEFAULT_MESSAGE_REQUEST = 'The request is invalid'

function handleError(serverError: any) {
  if (isObject(serverError)) {
    Object.entries(serverError).forEach(([, value]) => {
      const errorMessage = isEmpty(value) ? API_DEFAULT_MESSAGE_REQUEST : value
      toast.error(`${errorMessage}`)
    })
  }
}

const onRequest = (config: AxiosRequestConfig): AxiosRequestConfig => {
  return config
}

const onResponseError = (error: AxiosError): Promise<AxiosError> => {
  handleError(error?.response?.data)
  return Promise.reject(error)
}

export default function setupInterceptorsTo(axiosInstance: AxiosInstance): AxiosInstance {
  axiosInstance.interceptors.request.use(onRequest, undefined)

  axiosInstance.interceptors.response.use(undefined, onResponseError)

  return axiosInstance
}

```

Here is where we actually send a request to the server in this case a post request.

```js

import { BehaviorSubject } from 'rxjs'
import { isNull } from 'lodash'
import httpClient from '../../shared/http-client'

interface LoginRequestModel {
  email: string
  password: string
}

const currentUserSubject = isNull(localStorage.getItem('current_user'))
  ? new BehaviorSubject(null)
  : new BehaviorSubject(JSON.parse(localStorage.getItem('current_user')!))

export const currentUserValue = currentUserSubject.value

export async function login(requestData: LoginRequestModel): Promise<string> {
  const response = await httpClient.post('/auth/login', requestData)

  const { access_token: accesstoken } = response.data

  return accesstoken
}
```

Finally you can call your login function on your SignIn component and the only thing you need to do is to use it in an async way where ``await login(data)``. Thats the only input it gets since above you have the schema for this function and it does all the work for us isolated.