/** * API Client with Validation * FE-TYPE-002: Enhanced API client methods with automatic Zod validation * * Provides typed API client methods that automatically validate responses * using Zod schemas. */ import { AxiosResponse } from 'axios'; import { z } from 'zod'; import { apiClient } from './client'; import { safeValidateApiResponse } from '@/schemas/apiSchemas'; import { safeValidateApiRequest } from '@/schemas/apiRequestSchemas'; import { logger } from '@/utils/logger'; // Extend InternalAxiosRequestConfig to include validation schemas interface ValidatedRequestConfig { _requestSchema?: z.ZodSchema; _responseSchema?: z.ZodSchema; [key: string]: any; } /** * Create a validated API request * * @param requestFn - Function that makes the API request * @param schema - Zod schema to validate the response * @param options - Validation options * @returns Validated response */ export async function validatedRequest( requestFn: () => Promise>, schema: z.ZodSchema, options: { throwOnError?: boolean; } = {}, ): Promise { const { throwOnError = false } = options; const response = await requestFn(); const validation = safeValidateApiResponse(schema, response.data); if (!validation.success) { const errorMessage = `API response validation failed: ${validation.error?.errors.map(e => e.message).join(', ')}`; logger.error('[API Validation]', { errors: validation.error?.errors, data: response.data, }); if (throwOnError) { throw new Error(errorMessage); } // In development, log warning but continue if (import.meta.env.DEV) { console.warn('[API Validation Warning]', validation.error); } } // Return validated data if validation succeeded, otherwise return original data return validation.data ?? (response.data as T); } /** * Validated API client methods * Automatically validates responses using provided schemas */ export const validatedApiClient = { /** * GET request with validation */ get: ( url: string, schema: z.ZodSchema, config?: ValidatedRequestConfig, ): Promise => { return validatedRequest( () => apiClient.get(url, { ...config, _responseSchema: schema } as any), schema, ); }, /** * GET request with request params validation */ getWithParams: ( url: string, paramsSchema: z.ZodSchema

, responseSchema: z.ZodSchema, params?: P, config?: ValidatedRequestConfig, ): Promise => { // Validate params if (params) { const validation = safeValidateApiRequest(paramsSchema, params); if (!validation.success) { throw new Error(`Request params validation failed: ${validation.error?.errors.map(e => e.message).join(', ')}`); } params = validation.data; } return validatedRequest( () => apiClient.get(url, { ...config, params, _responseSchema: responseSchema } as any), responseSchema, ); }, /** * POST request with validation */ post: ( url: string, data?: D, requestSchema?: z.ZodSchema, responseSchema?: z.ZodSchema, config?: ValidatedRequestConfig, ): Promise => { // Validate request data if schema provided let validatedData = data; if (requestSchema && data !== undefined && data !== null) { const validation = safeValidateApiRequest(requestSchema, data); if (!validation.success) { throw new Error(`Request validation failed: ${validation.error?.errors.map(e => e.message).join(', ')}`); } validatedData = validation.data; } if (!responseSchema) { // If no response schema provided, use regular client return apiClient.post(url, validatedData, { ...config, _requestSchema: requestSchema } as any).then((res) => res.data); } return validatedRequest( () => apiClient.post(url, validatedData, { ...config, _requestSchema: requestSchema, _responseSchema: responseSchema } as any), responseSchema, ); }, /** * PUT request with validation */ put: ( url: string, data?: any, schema?: z.ZodSchema, config?: ValidatedRequestConfig, ): Promise => { if (!schema) { return apiClient.put(url, data, config as any).then((res) => res.data); } return validatedRequest( () => apiClient.put(url, data, { ...config, _responseSchema: schema } as any), schema, ); }, /** * PATCH request with validation */ patch: ( url: string, data?: any, schema?: z.ZodSchema, config?: ValidatedRequestConfig, ): Promise => { if (!schema) { return apiClient.patch(url, data, config as any).then((res) => res.data); } return validatedRequest( () => apiClient.patch(url, data, { ...config, _responseSchema: schema } as any), schema, ); }, /** * DELETE request with validation */ delete: ( url: string, schema?: z.ZodSchema, config?: ValidatedRequestConfig, ): Promise => { if (!schema) { return apiClient.delete(url, config as any).then((res) => res.data); } return validatedRequest( () => apiClient.delete(url, { ...config, _responseSchema: schema } as any), schema, ); }, };