import { ZodError, type ZodSchema } from 'zod'; export async function parseApiResponse( res: Response, schema: ZodSchema, ): Promise { 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レスポンスのパースに失敗しました'); } }