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.saveKey | Key of localstorage where token will be saved |
|---|---|
| tokenSettings.accessLifetime | Access token lifetime, api manager check if token still fresh before each request |
| tokenSettings.refreshLifetime | Refresh token lifetime, if refresh token is spoiled, there probably will be logout event |
| tokenSettings.transitionToNew | Temp 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.refresher | Function that responsible for refreshing token |
| requestMiddleware | Middleware 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,
}),
})