import type { FetchOptions } from 'ofetch' import { $fetch, FetchError } from 'ofetch' import { useAuthStore } from '~/stores/auth' export type AnyObject = Record export type ApiClient = { get(url: string, query?: AnyObject, options?: ApiFetchOptions<'json'>): Promise getBlob(url: string, query?: AnyObject, options?: ApiFetchOptions<'blob'>): Promise post(url: string, body?: AnyObject, options?: ApiFetchOptions<'json'>): Promise put(url: string, body?: AnyObject, options?: ApiFetchOptions<'json'>): Promise patch(url: string, body?: AnyObject, options?: ApiFetchOptions<'json'>): Promise delete(url: string, query?: AnyObject, options?: ApiFetchOptions<'json'>): Promise } export type ApiFetchOptions = FetchOptions & { toast?: boolean toastTitle?: string toastErrorMessage?: string toastSuccessMessage?: string toastErrorKey?: string toastSuccessKey?: string } export const useApi = (): ApiClient => { const config = useRuntimeConfig() const baseURL = config.public.apiBase ?? '/api' const toast = useToast() const auth = useAuthStore() const nuxtApp = useNuxtApp() const i18n = nuxtApp.$i18n as | { t: (key: string) => string te?: (key: string) => boolean } | undefined const t = (key: string) => (i18n?.t ? String(i18n.t(key)) : key) const te = (key: string) => (i18n?.te ? i18n.te(key) : false) const extractErrorMessage = (error: unknown, responseData?: unknown): string => { const data = responseData ?? (error as FetchError)?.data if (typeof data === 'string') { return data } if (data && typeof data === 'object') { const record = data as Record return ( (record['hydra:description'] as string) || (record.detail as string) || (record.message as string) || (record.error as string) || (record.title as string) || (record['hydra:title'] as string) || '' ) } return (error as FetchError)?.message ?? 'Erreur inconnue.' } const methodErrorKeys: Record = { GET: 'errors.http.get', POST: 'errors.http.post', PUT: 'errors.http.put', PATCH: 'errors.http.patch', DELETE: 'errors.http.delete' } const client = $fetch.create({ baseURL, retry: 0, credentials: 'include', onResponse({ options, response }) { const apiOptions = options as ApiFetchOptions<'json'> if (apiOptions?.toast === false) { return } if (response?.status && response.status >= 400) { return } const successKey = apiOptions?.toastSuccessKey const successMessage = apiOptions?.toastSuccessMessage || (successKey ? (te(successKey) ? t(successKey) : successKey) : '') if (successMessage) { toast.success({ title: 'Succès', message: successMessage }) } }, onResponseError({ response, error, options }) { const apiOptions = options as ApiFetchOptions<'json'> if (apiOptions?.toast === false) { return } const method = typeof options?.method === 'string' ? options.method.toUpperCase() : 'GET' const defaultKey = methodErrorKeys[method] const defaultMessage = defaultKey && te(defaultKey) ? t(defaultKey) : '' const errorKey = apiOptions?.toastErrorKey const errorMessage = errorKey ? (te(errorKey) ? t(errorKey) : errorKey) : '' const extractedMessage = extractErrorMessage(error, response?._data) const message = apiOptions?.toastErrorMessage || errorMessage || defaultMessage || extractedMessage || 'Une erreur est survenue.' toast.error({ title: apiOptions?.toastTitle ?? 'Erreur', message }) } }) const request = ( method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE', url: string, options: ApiFetchOptions<'json'> = {} ) => { const needsJsonBody = method === 'POST' || method === 'PUT' const needsMergePatch = method === 'PATCH' const headers = new Headers(options.headers as HeadersInit | undefined) if (needsMergePatch && !headers.has('Content-Type')) { headers.set('Content-Type', 'application/merge-patch+json') } else if (needsJsonBody && !headers.has('Content-Type')) { headers.set('Content-Type', 'application/json') } return client(url, { ...options, method, headers }) } return { get(url: string, query: AnyObject = {}, options: ApiFetchOptions<'json'> = {}) { return request('GET', url, { ...options, query }) }, getBlob(url: string, query: AnyObject = {}, options: ApiFetchOptions<'blob'> = {}) { return client(url, { ...options, method: 'GET', query, responseType: 'blob' }) }, post(url: string, body: AnyObject = {}, options: ApiFetchOptions<'json'> = {}) { return request('POST', url, { ...options, body }) }, put(url: string, body: AnyObject = {}, options: ApiFetchOptions<'json'> = {}) { return request('PUT', url, { ...options, body }) }, patch(url: string, body: AnyObject = {}, options: ApiFetchOptions<'json'> = {}) { return request('PATCH', url, { ...options, body }) }, delete(url: string, query: AnyObject = {}, options: ApiFetchOptions<'json'> = {}) { return request('DELETE', url, { ...options, query }) } } }