Files
bookhoard/web/src/api.ts
T
john-okeefe 60c5a093b5 feat(typescript): add core infrastructure modules
- Add centralized API type definitions (types/api.d.ts)
  - Interfaces for all API responses matching Go handler JSON
  - Snake_case field names matching actual API responses
  - Source file references in comments for verification

- Add API client module (api.ts)
  - Procedural get/post/put/delete functions
  - Automatic auth header injection
  - Exported to window for cross-module access

- Add DOM utilities (dom.ts)
  - escapeHtml for safe HTML rendering
  - querySelector wrappers with null checks
  - Element creation helpers

- Add event delegation helpers (events.ts)
  - Reusable event delegation pattern
  - Data attribute selectors for dynamic content

- Add localStorage wrapper (storage.ts)
  - Type-safe token management
  - Theme persistence helpers
2026-02-18 16:40:29 -05:00

102 lines
2.6 KiB
TypeScript

function getAuthHeader(): string {
const token = localStorage.getItem('token');
return token ? `Bearer ${token}` : '';
}
async function apiGet(url: string): Promise<Response> {
return fetch(`/api${url}`, {
headers: {
'Authorization': getAuthHeader(),
'Content-Type': 'application/json'
}
});
}
async function apiPost(url: string, data?: unknown): Promise<Response> {
return fetch(`/api${url}`, {
method: 'POST',
headers: {
'Authorization': getAuthHeader(),
'Content-Type': 'application/json'
},
body: data ? JSON.stringify(data) : undefined
});
}
async function apiPut(url: string, data?: unknown): Promise<Response> {
return fetch(`/api${url}`, {
method: 'PUT',
headers: {
'Authorization': getAuthHeader(),
'Content-Type': 'application/json'
},
body: data ? JSON.stringify(data) : undefined
});
}
async function apiDelete(url: string): Promise<Response> {
return fetch(`/api${url}`, {
method: 'DELETE',
headers: {
'Authorization': getAuthHeader()
}
});
}
async function apiPatch(url: string, data?: unknown): Promise<Response> {
return fetch(`/api${url}`, {
method: 'PATCH',
headers: {
'Authorization': getAuthHeader(),
'Content-Type': 'application/json'
},
body: data ? JSON.stringify(data) : undefined
});
}
async function handleResponse<T>(response: Response): Promise<T> {
if (!response.ok) {
const errorData = await response.json().catch(() => ({ error: 'Unknown error' }));
throw new Error(errorData.error || `HTTP ${response.status}`);
}
return response.json();
}
async function handleVoidResponse(response: Response): Promise<void> {
if (!response.ok) {
const errorData = await response.json().catch(() => ({ error: 'Unknown error' }));
throw new Error(errorData.error || `HTTP ${response.status}`);
}
}
function handleError(error: unknown, context: string): void {
console.error(`${context}:`, error);
const message = error instanceof Error ? error.message : 'An unexpected error occurred';
if ((window as any).showToast?.error) {
(window as any).showToast.error(message);
}
}
(window as any).api = {
get: apiGet,
post: apiPost,
put: apiPut,
delete: apiDelete,
patch: apiPatch,
handleResponse,
handleVoidResponse,
handleError
};
export {
getAuthHeader,
apiGet,
apiPost,
apiPut,
apiDelete,
apiPatch,
handleResponse,
handleVoidResponse,
handleError
};