import { ApisauceInstance, create, ApiResponse } from 'apisauce' import { getGeneralApiProblem } from './api-problem' import { ApiConfig, DEFAULT_API_CONFIG } from './api-config' import * as Types from './api.types' /** * Manages all requests to the API. */ export class Api { /** * The underlying apisauce instance which performs the requests. */ apisauce: ApisauceInstance /** * Configurable options. */ config: ApiConfig /** * Creates the api. * * @param config The configuration to use. */ constructor(config: ApiConfig = DEFAULT_API_CONFIG) { this.config = config } /** * Sets up the API. This will be called during the bootup * sequence and will happen before the first React component * is mounted. * * Be as quick as possible in here. */ setup() { // construct the apisauce instance this.apisauce = create({ baseURL: this.config.url, timeout: this.config.timeout, headers: { Accept: 'application/json', }, }) } /** * Gets a list of users. */ async getUsers(): Promise { // make the api call const response: ApiResponse = await this.apisauce.get(`/users`) // the typical ways to die when calling an api if (!response.ok) { const problem = getGeneralApiProblem(response) if (problem) return problem } const convertUser = (raw) => { return { id: raw.id, name: raw.name, } } // transform the data into the format we are expecting try { const rawUsers = response.data const resultUsers: Types.User[] = rawUsers.map(convertUser) return { kind: 'ok', users: resultUsers } } catch { return { kind: 'bad-data' } } } /** * Gets a single user by ID */ async getUser(id: string): Promise { // make the api call const response: ApiResponse = await this.apisauce.get(`/users/${id}`) // the typical ways to die when calling an api if (!response.ok) { const problem = getGeneralApiProblem(response) if (problem) return problem } // transform the data into the format we are expecting try { const resultUser: Types.User = { id: response.data.id, name: response.data.name, } return { kind: 'ok', user: resultUser } } catch { return { kind: 'bad-data' } } } }