88 lines
2.8 KiB
TypeScript
88 lines
2.8 KiB
TypeScript
import { ContainerManager } from '@/lib/di/container';
|
|
|
|
export interface FetchResult<T> {
|
|
data: T | null;
|
|
errors: Record<string, Error>;
|
|
hasErrors: boolean;
|
|
}
|
|
|
|
export class PageDataFetcher {
|
|
/**
|
|
* Fetch data using DI container
|
|
* Use for: Simple SSR pages with single service
|
|
* WARNING: Container is singleton - avoid stateful services
|
|
*/
|
|
static async fetch<TService, TMethod extends keyof TService>(
|
|
ServiceToken: string | symbol,
|
|
method: TMethod,
|
|
...args: TService[TMethod] extends (...params: infer P) => Promise<infer R> ? P : never
|
|
): Promise<(TService[TMethod] extends (...params: any[]) => Promise<infer R> ? R : never) | null> {
|
|
try {
|
|
const container = ContainerManager.getInstance().getContainer();
|
|
const service = container.get<TService>(ServiceToken);
|
|
const result = await (service[method] as Function)(...args);
|
|
return result;
|
|
} catch (error) {
|
|
console.error(`Failed to fetch: ${String(ServiceToken)}.${String(method)}`, error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fetch using manual service instantiation
|
|
* Use for: Multiple dependencies, request-scoped services, or auth context
|
|
* RECOMMENDED for SSR over fetch() with DI
|
|
*/
|
|
static async fetchManual<TData>(
|
|
serviceFactory: () => Promise<TData> | TData
|
|
): Promise<TData | null> {
|
|
try {
|
|
const result = await serviceFactory();
|
|
return result;
|
|
} catch (error) {
|
|
console.error('Failed to fetch manual:', error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fetch multiple datasets in parallel with error aggregation
|
|
* Use for: Pages needing multiple service calls
|
|
* UPDATED: Returns both data and errors for proper handling
|
|
*/
|
|
static async fetchMultiple<T extends Record<string, any>>(
|
|
queries: T
|
|
): Promise<FetchResult<{ [K in keyof T]: T[K] }>> {
|
|
const results = {} as { [K in keyof T]: T[K] };
|
|
const errors = {} as Record<string, Error>;
|
|
|
|
const entries = await Promise.all(
|
|
Object.entries(queries).map(async ([key, query]) => {
|
|
try {
|
|
const result = await query();
|
|
return [key, { success: true, data: result }] as const;
|
|
} catch (error) {
|
|
console.error(`Failed to fetch ${key}:`, error);
|
|
return [key, { success: false, error: error instanceof Error ? error : new Error(String(error)) }] as const;
|
|
}
|
|
})
|
|
);
|
|
|
|
entries.forEach(([key, result]) => {
|
|
if (typeof result === 'object' && result !== null && 'success' in result) {
|
|
if (result.success) {
|
|
results[key as keyof T] = (result as { data: T[keyof T] }).data;
|
|
} else {
|
|
errors[key] = (result as { error: Error }).error;
|
|
}
|
|
}
|
|
});
|
|
|
|
return {
|
|
data: results,
|
|
errors,
|
|
hasErrors: Object.keys(errors).length > 0
|
|
};
|
|
}
|
|
}
|