captains-log/frontend/app/services/api.ts
Drew Galbraith 1dfc08407c
Some checks failed
Docker Build / Build and Push Backend Image (push) Failing after 39s
Docker Build / Build and Push Frontend Image (push) Has been cancelled
Check / Frontend (pull_request) Has been cancelled
Check / Backend (pull_request) Has been cancelled
Add a docker build step.
2025-09-28 22:24:08 -07:00

115 lines
2.8 KiB
TypeScript

import type {
Task,
TaskStatus,
CreateTaskRequest,
UpdateTaskRequest,
ApiError,
} from '~/types/task'
const API_BASE_URL = `${import.meta.env.VITE_API_URL || 'http://localhost:3000'}/api`
class ApiClient {
private async fetchWrapper<T>(
endpoint: string,
options: RequestInit = {}
): Promise<T> {
const url = `${API_BASE_URL}${endpoint}`
const config: RequestInit = {
headers: {
'Content-Type': 'application/json',
...options.headers,
},
...options,
}
if (process.env.NODE_ENV === 'development') {
console.log(`API Request: ${config.method || 'GET'} ${url}`, {
body: config.body,
headers: config.headers,
})
}
try {
const response = await fetch(url, config)
if (process.env.NODE_ENV === 'development') {
console.log(`API Response: ${response.status} ${response.statusText}`, {
url,
status: response.status,
})
}
if (!response.ok) {
let errorMessage = `HTTP ${response.status}: ${response.statusText}`
try {
const errorData = await response.json()
errorMessage = errorData.message || errorMessage
} catch {
// If JSON parsing fails, use the default error message
}
throw new Error(errorMessage)
}
if (response.status === 204) {
return null as T
}
const data = await response.json()
if (process.env.NODE_ENV === 'development') {
console.log('API Response Data:', data)
}
return data
} catch (error) {
const apiError: ApiError = {
message:
error instanceof Error ? error.message : 'Unknown error occurred',
status:
error instanceof Error && 'status' in error
? (error as { status?: number }).status
: undefined,
}
if (process.env.NODE_ENV === 'development') {
console.error('API Error:', apiError)
}
throw apiError
}
}
async listTasks(status?: TaskStatus): Promise<Task[]> {
const url = status ? `/tasks?status=${status}` : '/tasks'
return this.fetchWrapper<Task[]>(url)
}
async getTask(id: string): Promise<Task> {
return this.fetchWrapper<Task>(`/tasks/${id}`)
}
async createTask(data: CreateTaskRequest): Promise<Task> {
return this.fetchWrapper<Task>('/tasks', {
method: 'POST',
body: JSON.stringify(data),
})
}
async updateTask(id: string, data: UpdateTaskRequest): Promise<Task> {
return this.fetchWrapper<Task>(`/tasks/${id}`, {
method: 'PUT',
body: JSON.stringify(data),
})
}
async deleteTask(id: string): Promise<void> {
return this.fetchWrapper<void>(`/tasks/${id}`, {
method: 'DELETE',
})
}
}
export const apiClient = new ApiClient()