turash/bugulma/frontend/lib/api-service-factory.ts
Damir Mukimov 6347f42e20
Consolidate repositories: Remove nested frontend .git and merge into main repository
- Remove nested git repository from bugulma/frontend/.git
- Add all frontend files to main repository tracking
- Convert from separate frontend/backend repos to unified monorepo
- Preserve all frontend code and development history as tracked files
- Eliminate nested repository complexity for simpler development workflow

This creates a proper monorepo structure with frontend and backend
coexisting in the same repository for easier development and deployment.
2025-11-25 06:02:57 +01:00

122 lines
3.9 KiB
TypeScript

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