UNPKG

api-wizard

Version:

A powerful TypeScript wrapper for native Fetch API with token management, interceptors, and type-safe HTTP requests

304 lines (241 loc) 7.17 kB
# API Wizard 🧙‍♂️ A powerful TypeScript wrapper for native Fetch API that provides enhanced features including token management, interceptors, and type-safe HTTP requests. ## Features - 🔒 Built-in token management (access & refresh tokens) - 🎯 Type-safe HTTP requests - 🔄 Automatic token refresh - 🎨 Customizable interceptors - 🛠 Flexible configuration options - 📝 Content-type and charset management - 🌐 Native Fetch API (smaller bundle size) - ⚡ Zero dependencies ## Installation ```bash npm install api-wizard # or yarn add api-wizard ``` ## Basic Usage ```typescript import { handler } from 'api-wizard'; // Define API endpoints configuration const apiConfig: Record<string, string> = { users: 'https://api.users.com', products: 'https://api.products.com' }; // Create API handlers const api = handler(apiConfig); // Make type-safe requests interface User { id: number; name: string; } // GET request with version and without version const userApi = api.users({version: 'v1'}); // https://api.users.com/v1 const legacyUserApi = api.users(); // https://api.users.com const getUser = async (id: number) => { const response = await userApi.get<User>(`/users/${id}`); return response.data; }; // POST request with type-safe request body interface CreateUserRequest { name: string; email: string; } const createUser = async (userData: CreateUserRequest) => { const response = await userApi.post<CreateUserRequest, User>('/users', userData); return response.data; }; ``` ## Advanced Configuration ### Token Management ```typescript const userApi = api.users({ version: 'v1', interceptor: { tokenConfig: { // Token storage configuration getToken: () => localStorage.getItem('access_token'), setToken: (token) => localStorage.setItem('access_token', token), removeToken: () => localStorage.removeItem('access_token'), // Refresh token configuration getRefreshToken: () => localStorage.getItem('refresh_token'), setRefreshToken: (token) => localStorage.setItem('refresh_token', token), removeRefreshToken: () => localStorage.removeItem('refresh_token'), // Endpoints refreshEndpoint: '/auth/refresh', // Optional: Custom header format formatAuthHeader: (token) => ({ 'Authorization': `Custom ${token}` }), // Optional: Token expiry callback onTokenExpired: () => { // Handle token expiration (e.g., redirect to login) } } } }); ``` ### Custom Interceptors ```typescript const userApi = api.users({ version: 'v1', interceptor: { onRequest: (config) => { // Modify request config config.headers['Custom-Header'] = 'value'; return config; }, onResponse: (response) => { // Transform response data response.data = response.data.result; return response; }, onError: async (error) => { // Custom error handling if (error.response?.status === 404) { // Handle 404 error } return Promise.reject(error); } } }); ``` ### API Versioning & Content Type ```typescript const userApi = api.users({ // API version will be added to base URL version: 'v1', // Content type configuration contentType: 'application/json', charset: 'UTF-8', accept: 'application/json', // Credentials withCredentials: true // default: true }); // Results in: https://api.example.com/v1 // Headers: // Content-Type: application/json; charset=UTF-8 // Accept: application/json // Credentials: include ``` ## Type Definitions ### Option Interface ```typescript interface Option { version?: string; contentType?: DataType; accept?: DataType; charset?: string; interceptor?: Interceptor; withCredentials?: boolean; requestConfig?: FetchRequestConfig; } ``` ### TokenConfig Interface ```typescript interface TokenConfig { accessTokenKey?: string; refreshTokenKey?: string; refreshEndpoint?: string; getToken?: () => string | undefined; setToken?: (token: string) => void; removeToken?: () => void; getRefreshToken?: () => string | undefined; setRefreshToken?: (token: string) => void; removeRefreshToken?: () => void; onTokenExpired?: () => void; formatAuthHeader?: (token: string, refreshToken?: string) => Record<string, string>; } ``` ### DataType Options ```typescript type DataType = | "application/json" | "application/x-www-form-urlencoded" | "application/xml" | "application/octet-stream" | "multipart/form-data" | "text/plain" | "text/html"; ``` ## Error Handling API Wizard provides structured error handling with detailed error information: ```typescript try { const response = await api.get('/protected-resource'); // Handle success } catch (error) { if (error.response) { // Server responded with error status console.error('Status:', error.response.status); console.error('Data:', error.response.data); } else if (error.request) { // Request was made but no response received console.error('No response received'); } else { // Error in setting up the request console.error('Error:', error.message); } } ``` ## Key Features ### Automatic Token Refresh API Wizard automatically handles token refresh when tokens expire: - Detects 401 responses - Automatically calls refresh endpoint - Retries original request with new token - Handles concurrent requests during refresh ### Type Safety Full TypeScript support with generic types: ```typescript // Request and response types are fully typed const response = await api.post<CreateUserRequest, User>('/users', userData); // response.data is typed as User ``` ### Flexible Configuration Configure different options for different APIs: ```typescript const api = handler({ users: 'https://api.users.com', payments: 'https://api.payments.com' }, { // Global configuration for all APIs withCredentials: true, contentType: 'application/json' }); // API-specific configuration const usersApi = api.users({ version: 'v1', interceptor: { /* user-specific interceptors */ } }); const paymentsApi = api.payments({ version: 'v2', contentType: 'application/xml' }); ``` ## API Methods All standard HTTP methods are supported: ```typescript const api = handler({ base: 'https://api.example.com' }).base({version: 'v1'}); // GET const users = await api.get<User[]>('/users'); // POST const newUser = await api.post<CreateUserRequest, User>('/users', userData); // PUT const updatedUser = await api.put<UpdateUserRequest, User>(`/users/${id}`, userData); // PATCH const patchedUser = await api.patch<Partial<User>, User>(`/users/${id}`, partialData); // DELETE await api.delete(`/users/${id}`); ``` ## Performance Benefits - **Zero Dependencies**: No external libraries required - **Small Bundle Size**: Lightweight implementation - **Native Fetch**: Uses browser's native HTTP client - **Tree Shaking**: Only import what you need - **Modern JavaScript**: Built for modern browsers ## Contributing Contributions are welcome! Please feel free to submit a Pull Request. ## License MIT