meals-made-easy-react-app/src/api/apiCallFactory.ts
2024-08-21 10:20:12 -05:00

63 lines
2.1 KiB
TypeScript

import AccessToken from '../types/AccessToken'
import Refresh from '../types/Refresh'
import { ApiError } from './ApiError'
export interface ApiCallDeps {
accessToken: AccessToken | null
endpoint: string
query?: string
refresh: Refresh
signal: AbortSignal
body?: any
}
export type Method = 'GET' | 'POST' | 'PUT' | 'DELETE'
const getApiCallFactory =
(method: Method) =>
<T>(handleBody?: (raw: any) => T) =>
async ({ accessToken, endpoint, refresh, signal, body }: ApiCallDeps): Promise<T> => {
const headers = new Headers()
if (accessToken) {
headers.set('Authorization', `Bearer ${accessToken.token}`)
}
if (body) {
headers.set('Content-type', 'application/json')
}
const url = import.meta.env.VITE_MME_API_URL + endpoint
const response = await fetch(url, {
body: body ? JSON.stringify(body) : undefined,
signal,
headers,
mode: 'cors',
method
})
if (response.ok && handleBody) {
return handleBody(await response.json())
} else if (response.status === 401) {
const newToken = await refresh()
if (newToken === null) {
throw new ApiError(401, 'Could not get a refreshed access token.')
}
headers.set('Authorization', `Bearer ${newToken.token}`)
const retry = await fetch(url, {
signal,
headers,
mode: 'cors',
method
})
if (retry.ok && handleBody) {
return handleBody(await retry.json())
} else {
throw new ApiError(retry.status, retry.statusText)
}
} else {
throw new ApiError(response.status, response.statusText)
}
}
export const getCallFactory = getApiCallFactory('GET')
export const postCallFactory = getApiCallFactory('POST')
export const putCallFactory = getApiCallFactory('PUT')
export const deleteCallFactory = getApiCallFactory('DELETE')