FE_Dev / api-client /utils /parseApiResponse.ts
GitHub Actions
Deploy from GitHub Actions [dev] - 2025-10-31 07:28:50
68f7925
import { ZodError, type ZodSchema } from 'zod';
export async function parseApiResponse<T>(
res: Response,
schema: ZodSchema<T>,
): Promise<T> {
if (!res.ok) {
let errorMessage = `APIリクエストに失敗しました (${res.status})`;
try {
const contentType = res.headers.get('content-type');
if (contentType?.includes('application/json')) {
const errorData = await res.json();
if (errorData?.error?.message) {
errorMessage += `: ${errorData.error.message}`;
}
}
} catch {
// エラーレスポンスの読み取りに失敗
}
throw new Error(errorMessage);
}
const responseData = await res.json();
// nullの場合はそのまま返す(NOT_FOUNDの場合)
if (responseData === null) {
return null as T;
}
// バックエンドが { data: T } 形式で返す場合の処理
const data =
responseData?.data !== undefined ? responseData.data : responseData;
try {
return schema.parse(data);
} catch (error) {
if (error instanceof ZodError) {
// URLからAPIエンドポイント名を抽出
const urlObj = new URL(res.url);
const apiEndpoint = urlObj.pathname.replace('/api/rpc/', '');
console.error(`[${apiEndpoint}] Zod validation error:`);
console.error('API:', apiEndpoint);
console.error('URL:', res.url);
console.error('Status:', res.status);
console.error('Schema:', (schema as any)._def?.typeName || 'unknown');
console.error('Validation errors:');
error.errors.forEach((err, index) => {
console.error(` Error ${index + 1}:`);
console.error(` Path: ${err.path.join('.')}`);
console.error(` Message: ${err.message}`);
console.error(` Code: ${err.code}`);
if ('expected' in err && err.expected !== undefined) {
console.error(` Expected: ${err.expected}`);
}
if ('received' in err && err.received !== undefined) {
console.error(` Received: ${err.received}`);
}
});
console.error('Response data:', JSON.stringify(responseData, null, 2));
console.error('Processed data:', JSON.stringify(data, null, 2));
throw error;
}
throw new Error('APIレスポンスのパースに失敗しました');
}
}