151 lines
3.2 KiB
JavaScript
151 lines
3.2 KiB
JavaScript
/**
|
|
* API Client
|
|
* Fetch wrappers for /api/* endpoints with offline support
|
|
*/
|
|
|
|
class APIClient {
|
|
constructor(baseURL = '/api') {
|
|
this.baseURL = baseURL;
|
|
this.interceptors = {
|
|
request: [],
|
|
response: []
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Add request interceptor
|
|
* @param {Function} interceptor - (config) => config
|
|
*/
|
|
addRequestInterceptor(interceptor) {
|
|
this.interceptors.request.push(interceptor);
|
|
}
|
|
|
|
/**
|
|
* Add response interceptor
|
|
* @param {Function} interceptor - (response) => response
|
|
*/
|
|
addResponseInterceptor(interceptor) {
|
|
this.interceptors.response.push(interceptor);
|
|
}
|
|
|
|
/**
|
|
* Execute request interceptors
|
|
* @param {RequestInit} config
|
|
* @returns {RequestInit}
|
|
*/
|
|
_applyRequestInterceptors(config) {
|
|
return this.interceptors.request.reduce(
|
|
(acc, interceptor) => interceptor(acc),
|
|
config
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Execute response interceptors
|
|
* @param {Response} response
|
|
* @returns {Response}
|
|
*/
|
|
_applyResponseInterceptors(response) {
|
|
return this.interceptors.response.reduce(
|
|
(acc, interceptor) => interceptor(acc),
|
|
response
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Resolve an endpoint against the configured API base URL
|
|
* @param {string} endpoint
|
|
* @returns {string}
|
|
*/
|
|
resolveURL(endpoint = '') {
|
|
return `${this.baseURL}${endpoint}`;
|
|
}
|
|
|
|
/**
|
|
* Replace the configured API base URL for future requests.
|
|
* @param {string} baseURL
|
|
*/
|
|
setBaseURL(baseURL = '/api') {
|
|
this.baseURL = baseURL || '/api';
|
|
}
|
|
|
|
/**
|
|
* Make API request
|
|
* @param {string} endpoint
|
|
* @param {RequestInit} options
|
|
* @returns {Promise<Response>}
|
|
*/
|
|
async request(endpoint, options = {}) {
|
|
const url = this.resolveURL(endpoint);
|
|
const config = this._applyRequestInterceptors({
|
|
...options,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
...options.headers
|
|
}
|
|
});
|
|
|
|
try {
|
|
const response = await fetch(url, config);
|
|
return this._applyResponseInterceptors(response);
|
|
} catch (error) {
|
|
// TODO: Implement offline queue management
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* GET request
|
|
* @param {string} endpoint
|
|
* @param {RequestInit} options
|
|
* @returns {Promise<Response>}
|
|
*/
|
|
async get(endpoint, options = {}) {
|
|
return this.request(endpoint, { ...options, method: 'GET' });
|
|
}
|
|
|
|
/**
|
|
* POST request
|
|
* @param {string} endpoint
|
|
* @param {any} data
|
|
* @param {RequestInit} options
|
|
* @returns {Promise<Response>}
|
|
*/
|
|
async post(endpoint, data, options = {}) {
|
|
return this.request(endpoint, {
|
|
...options,
|
|
method: 'POST',
|
|
body: JSON.stringify(data)
|
|
});
|
|
}
|
|
|
|
/**
|
|
* PUT request
|
|
* @param {string} endpoint
|
|
* @param {any} data
|
|
* @param {RequestInit} options
|
|
* @returns {Promise<Response>}
|
|
*/
|
|
async put(endpoint, data, options = {}) {
|
|
return this.request(endpoint, {
|
|
...options,
|
|
method: 'PUT',
|
|
body: JSON.stringify(data)
|
|
});
|
|
}
|
|
|
|
/**
|
|
* DELETE request
|
|
* @param {string} endpoint
|
|
* @param {RequestInit} options
|
|
* @returns {Promise<Response>}
|
|
*/
|
|
async delete(endpoint, options = {}) {
|
|
return this.request(endpoint, { ...options, method: 'DELETE' });
|
|
}
|
|
}
|
|
|
|
// Export singleton instance
|
|
export const api = new APIClient();
|
|
|