/** * Generic API service factory * Reduces duplication in service layer by providing common CRUD operations */ import { z } from 'zod'; import { apiGet, apiPost, apiDelete } from '@/lib/api-client'; /** * Options for creating a CRUD API service */ export interface CreateCRUDServiceOptions< TEntity, TCreateRequest, TUpdateRequest = TCreateRequest, > { basePath: string; entitySchema: z.ZodSchema; createRequestSchema?: z.ZodSchema; updateRequestSchema?: z.ZodSchema; } /** * Result of creating a CRUD service */ export interface CRUDService { getById: (id: string) => Promise; list: (filters?: Record) => Promise; create: (request: TCreateRequest) => Promise; update: (id: string, request: TUpdateRequest) => Promise; delete: (id: string) => Promise; } /** * Creates a standard CRUD API service with validation * Handles common patterns: GET, POST, PUT, DELETE with Zod validation */ export function createCRUDService( options: CreateCRUDServiceOptions ): CRUDService { const { basePath, entitySchema, createRequestSchema, updateRequestSchema } = options; const getById = async (id: string): Promise => { const data = await apiGet(`${basePath}/${id}`); return entitySchema.parse(data); }; const list = async (filters?: Record): Promise => { const queryParams = new URLSearchParams(); if (filters) { Object.entries(filters).forEach(([key, value]) => { if (value !== undefined && value !== null) { if (Array.isArray(value)) { value.forEach((v) => queryParams.append(key, String(v))); } else { queryParams.append(key, String(value)); } } }); } const endpoint = `${basePath}${queryParams.toString() ? `?${queryParams.toString()}` : ''}`; const data = await apiGet(endpoint); return z.array(entitySchema).parse(data); }; const create = async (request: TCreateRequest): Promise => { const validated = createRequestSchema ? createRequestSchema.parse(request) : request; const data = await apiPost(basePath, validated); return entitySchema.parse(data); }; const update = async (id: string, request: TUpdateRequest): Promise => { const validated = updateRequestSchema ? updateRequestSchema.parse(request) : request; const data = await apiPost(`${basePath}/${id}`, validated); return entitySchema.parse(data); }; const deleteById = async (id: string): Promise => { await apiDelete(`${basePath}/${id}`); }; return { getById, list, create, update, delete: deleteById, }; } /** * Creates a service function with validation * Useful for non-CRUD endpoints */ export function createValidatedService( endpoint: string, requestSchema: z.ZodSchema, responseSchema: z.ZodSchema, method: 'GET' | 'POST' = 'POST' ) { return async (request: TRequest): Promise => { const validated = requestSchema.parse(request); let data: TResponse; if (method === 'GET') { const queryParams = new URLSearchParams(); Object.entries(validated as Record).forEach(([key, value]) => { if (value !== undefined && value !== null) { queryParams.append(key, String(value)); } }); const url = `${endpoint}${queryParams.toString() ? `?${queryParams.toString()}` : ''}`; data = await apiGet(url); } else { data = await apiPost(endpoint, validated); } return responseSchema.parse(data); }; }