Skip to main content

ApiManager

Api manager

Defining api manager


const NEW_SAVE_KEY = '@token_v2'

export const apiManager = new ApiManager(config.API_HOST, {
tokenSettings: {
saveKey: NEW_SAVE_KEY,
accessLifetime: Infinity,
transitionToNew: async (currentToken) => {
if (currentToken) {
if (currentToken.refresh) return currentToken
/**
* Here should be request to exchange old token to a new one
*/
return currentToken
}
const oldToken = localStorage.getItem(oldKey)
if (!oldToken) return null
return {
access: oldToken,
refresh: '',
startTime: Date.now(),
}
},
refresher: async (currentToken) => {
//request to backend to refresh token
return currentToken
},
},
requestMiddleware: (config) => {
if (!config.headers) config.headers = {}
config.headers['Accept-Language'] = getUserLocale().header
return config
},
})

tokenSettings.saveKeyKey of localstorage where token will be saved
tokenSettings.accessLifetimeAccess token lifetime, api manager check if token still fresh before each request
tokenSettings.refreshLifetimeRefresh token lifetime, if refresh token is spoiled, there probably will be logout event
tokenSettings.transitionToNewTemp prop, that responsible for transition from old token to a new one. So apiManager tries to get value from corresponding key (saveKey) and then pass obtained value to transitionToNew function. If there is no key in saved, transitionToNew tries to obtain old token. If it is present, object with empty refresh will be returned. Later we will have endpoint on backend to exchange old token to a new one. We check if token exists but refresh token is not present it means we can exchange token.
tokenSettings.refresherFunction that responsible for refreshing token
requestMiddlewareMiddleware for update axios config before each request

Building API

GET with no props


type GetAllClassesResponse = {}

const getAllClasses = apiManager.get<GetAllClassesResponse>(
'teachers/classes'
)
const classes = await getAllClasses()
//will send GET request to `${config.API_HOST}/teachers/classes`

GET with string / number props


type GetSingleClassesResponse = {}

const getSingleClass = apiManager.get<GetSingleClassesResponse, string | number>(
'teachers/classes'
)
const oneClass = await getSingleClass('111')
//will send GET request to `${config.API_HOST}/teachers/classes/111`

GET with object props


type GetAllClassesResponse = {}
type GetAllClassesPayload = {
fetchAll?: 1 | 0
sort?: 'asc' | 'desc'
}

const getAllClasses = apiManager.get<GetAllClassesResponse, GetAllClassesPayload | void>(
'teachers/classes'
)
const classes = await getAllClasses()
//will send GET request to `${config.API_HOST}/teachers/classes` we still can call without props, since we pass void

const classesSorted = await getAllClasses({
fetchAll: 1,
sort: 'asc'
})
//will send GET request to `${config.API_HOST}/teachers/classes?fetchAll=1&sort=asc` object payload will automatically goes to params

Mapping objects


type UpdateSingleClassResponse = {}
type UpdateSingleClassPayload = {
classId: string
teacherId: string
data: {
name?: string
description?: string
grade?: number
}
}

const updateSingleClass = apiManager.put<UpdateSingleClassResponse, UpdateSingleClassPayload>(({classId, teacherId, data}) => ({
url: `teachers/${teacherId}/classes/${classId}`,
body: data
}))
const classes = await updateSingleClass({
teacherId: '111',
classId: '222',
data: {
name: 'New name',
description: 'New description',
grade: 5
}
})
//will send PUT request to `${config.API_HOST}/teachers/111/classes/222` with body {name: 'New name', description: 'New description', grade: 5}

type GetTeacherClassesResponse = {}
type GetTeacherClassesPayload = {
teacherId: string
data: {
fetchAll?: 1 | 0
sort?: 'asc' | 'desc'
}
}

const getTeacherClasses = apiManager.get<GetTeacherClassesResponse, GetTeacherClassesPayload>(({teacherId, data}) => ({
url: `teachers/${teacherId}/classes`,
params: data
}))
//you can pass data to body as well, and they will be moved to params

const teacherClasses = await getTeacherClasses({
teacherId: '111',
data: {
fetchAll: 1,
sort: 'asc'
}
})
//will send GET request to `${config.API_HOST}/teachers/111/classes?fetchAll=1&sort=asc`

Unprotected endpoints


type LoginResponse = {}
type AutoLoginPayload = {}

const autologin = apiManager.unprotectedRequest<LoginResponse, AutoLoginPayload>(
ApiMethod.POST,
'autologin'
)
//Same set up flow as previously

Unprotected endpoints


type LoginResponse = {}
type AutoLoginPayload = {}

const autologin = apiManager.unprotectedRequest<LoginResponse, AutoLoginPayload>(
ApiMethod.POST,
'autologin'
)
//Same set up flow as previously

Deep setup


type SendSolutionStatusesResponse = {}
type SolutionStatusesPayload = {
token: string,
body: {
solutionId: string
status: 'accepted' | 'rejected' | 'pending'
}[]
}

const sendSolutionStatusesFromQueue = apiManager.post<
SendSolutionStatusesResponse,
SolutionStatusesPayload
>({
driver: 'fetch',
keepalive: true,
data: ({ token, body }) => ({
url: 'solution-statuses',
headers: {
Authorization: token,
},
body,
}),
})