UNPKG

@andranik-arakelyan/js-utilities

Version:
201 lines (200 loc) 7.75 kB
import { AxiosInstance, AxiosResponse } from 'axios'; /** * Configuration options for the BaseApiClient. * * @interface BaseApiClientConfig * @property {string} baseUrl - The base URL for all API requests * @property {string} [urlPrefix] - Optional URL prefix to append to baseUrl (e.g., '/v1' for versioning) * * @example * const config: BaseApiClientConfig = { * baseUrl: 'https://api.example.com', * urlPrefix: '/v1' * }; */ export interface BaseApiClientConfig { baseUrl: string; urlPrefix?: string; } /** * Base API client class providing common request handling functionality. * All specific API clients should extend this class. * * This class manages HTTP requests using axios and maintains custom headers * that are sent with every request. Headers are stored in the client instance * (not in axios defaults) for better reliability and explicit control. * * @abstract * @class BaseApiClient * * @example * class UserApiClient extends BaseApiClient { * async getUser(id: number) { * return this.request({ * path: `/users/${id}`, * method: 'GET' * }); * } * } * * const client = new UserApiClient({ * baseUrl: 'https://api.example.com', * urlPrefix: '/v1' * }); * * client.setHeaders({ 'Authorization': 'Bearer token123' }); * const user = await client.getUser(1); */ export declare abstract class BaseApiClient { protected config: BaseApiClientConfig; protected axiosInstance: AxiosInstance; protected customHeaders: Record<string, string>; /** * Creates a new base API client instance. * * Initializes the axios instance with the provided configuration and * sets up default headers (Content-Type: application/json). * Headers are managed in the client instance for explicit control. * * @param {BaseApiClientConfig} config - The configuration options for the API client * @param {string} config.baseUrl - The base URL for all API requests * @param {string} [config.urlPrefix] - Optional URL prefix (e.g., '/v1') * * @example * constructor(config: BaseApiClientConfig) { * super(config); * } */ constructor(config: BaseApiClientConfig); /** * Sets headers for all subsequent requests. * * Headers are stored in the client instance and passed explicitly with each request. * This provides better reliability compared to using axios default headers. * Headers set to null will be deleted. * * @param {Record<string, string | null>} headers - Object containing headers to set. * Use null as a value to delete a header. * * @example * // Set custom headers * client.setHeaders({ * 'X-Request-Id': 'req-123', * 'Authorization': 'Bearer token123' * }); * * // Remove a header * client.setHeaders({ 'X-Request-Id': null }); */ setHeaders(headers: Record<string, string | null>): void; /** * Gets the current headers. * * Returns a copy of all current headers stored in the client instance. * This includes default headers (Content-Type) and any custom headers set via setHeaders(). * * @returns {Record<string, string>} A copy of all current headers * * @example * const headers = client.getHeaders(); * console.log(headers['Content-Type']); // 'application/json' * console.log(headers['Authorization']); // 'Bearer token123' (if set) */ getHeaders(): Record<string, string>; /** * Makes an HTTP request to the API. * * Sends a request to the API with the specified configuration and current headers. * Headers are explicitly passed with each request for reliable header management. * Handles both successful responses and axios errors. * * When an error occurs, the full response data is bound to the Error instance via the * `response` property, allowing access to structured error information from the server. * * @template T The expected response data type * @param {Object} options - Request configuration options * @param {string} options.path - The API endpoint path (e.g., '/users/1') * @param {Record<string, string | number | undefined>} [options.query] - Optional query parameters * @param {any} [options.body] - Optional request body for POST/PUT/PATCH requests * @param {string} [options.method='GET'] - HTTP method (GET, POST, PUT, PATCH, DELETE) * @returns {Promise<T>} The parsed response data * @throws {Error} If the request fails, returns an error status code, or response has success: false. * The error object will have a `response` property containing the full error response data. * * @example * // GET request * const user = await this.request({ * path: '/users/1', * method: 'GET' * }); * * // Handling errors with structured error data * try { * await this.request({ path: '/users', method: 'POST', body: data }); * } catch (error: any) { * console.log(error.message); // 'Validation Error' * console.log(error.response.code); // 'VALIDATION_ERROR' * console.log(error.response.errors); // array of field errors * } * * // GET with query parameters * const users = await this.request({ * path: '/users', * method: 'GET', * query: { page: 1, limit: 10 } * }); */ protected request<T>(options: { path: string; query?: Record<string, string | number | undefined>; body?: any; method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE"; }): Promise<T>; /** * Handles API response parsing and error checking. * * Validates the response status code and checks for error indicators in the response data. * Supports two error patterns: * 1. HTTP error status codes (< 200 or >= 300) * 2. JSON response with success: false field * * Priority for error messages: error field > message field > statusText * * When an error is thrown, the response data is bound to the Error instance via the * `response` property, making the full error details accessible to callers. * * @template T The expected response data type * @param {AxiosResponse} response - The axios Response object * @returns {T} The parsed and validated response data * @throws {Error} If the response indicates an error or has success: false. * The error object will have a `response` property containing the response data. * * @example * // Successful response * const data = handleResponse({ status: 200, data: { id: 1, name: 'John' } }); * * // Error with success: false * // throws Error('Operation failed') with error.response = { success: false, error: 'Operation failed', ... } * const data = handleResponse({ * status: 200, * data: { success: false, error: 'Operation failed' } * }); * * // HTTP error status * // throws Error('Not Found') with error.response = { error: 'Resource not found', code: 'NOT_FOUND', ... } * const data = handleResponse({ * status: 404, * statusText: 'Not Found', * data: { error: 'Resource not found', code: 'NOT_FOUND' } * }); * * // Usage: * // try { * // await client.getUser(1); * // } catch (error: any) { * // console.log(error.message); // 'Not Found' * // console.log(error.response); // { error: 'Resource not found', code: 'NOT_FOUND', ... } * // } */ protected handleResponse<T>(response: AxiosResponse): T; }