apiClient.ts 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. import { API_BASE_URL } from '../utils/constants';
  2. interface ApiResponse<T = any> {
  3. data: T;
  4. status: number;
  5. }
  6. class ApiClient {
  7. private baseURL: string;
  8. constructor(baseURL: string) {
  9. this.baseURL = baseURL;
  10. }
  11. private getAuthHeaders(): Record<string, string> {
  12. const token = localStorage.getItem('authToken') || localStorage.getItem('token');
  13. const language = localStorage.getItem('userLanguage') || 'ja';
  14. // console.log('API 呼び出し token:', token ? token.substring(0, 10) + '...' : 'null');
  15. return {
  16. 'Content-Type': 'application/json',
  17. 'x-user-language': language,
  18. ...(token && { Authorization: `Bearer ${token}` }),
  19. };
  20. }
  21. // 新しい API 呼び出し方法、{ data, status } を返す
  22. async get<T = any>(url: string): Promise<ApiResponse<T>> {
  23. const response = await fetch(`${this.baseURL}${url}`, {
  24. method: 'GET',
  25. headers: this.getAuthHeaders(),
  26. });
  27. if (!response.ok) {
  28. const errorData = await response.json();
  29. throw new Error(errorData.message || 'Request failed');
  30. }
  31. const data = await response.json();
  32. return { data, status: response.status };
  33. }
  34. async post<T = any>(url: string, body?: any): Promise<ApiResponse<T>> {
  35. const response = await fetch(`${this.baseURL}${url}`, {
  36. method: 'POST',
  37. headers: this.getAuthHeaders(),
  38. body: body ? JSON.stringify(body) : undefined,
  39. });
  40. if (!response.ok) {
  41. const errorData = await response.json();
  42. throw new Error(errorData.message || 'Request failed');
  43. }
  44. const data = await response.json();
  45. return { data, status: response.status };
  46. }
  47. async put<T = any>(url: string, body?: any): Promise<ApiResponse<T>> {
  48. const response = await fetch(`${this.baseURL}${url}`, {
  49. method: 'PUT',
  50. headers: this.getAuthHeaders(),
  51. body: body ? JSON.stringify(body) : undefined,
  52. });
  53. if (!response.ok) {
  54. const errorData = await response.json();
  55. throw new Error(errorData.message || 'Request failed');
  56. }
  57. const data = await response.json();
  58. return { data, status: response.status };
  59. }
  60. async delete<T = any>(url: string): Promise<ApiResponse<T>> {
  61. const response = await fetch(`${this.baseURL}${url}`, {
  62. method: 'DELETE',
  63. headers: this.getAuthHeaders(),
  64. });
  65. if (!response.ok) {
  66. const errorData = await response.json();
  67. throw new Error(errorData.message || 'Request failed');
  68. }
  69. const data = await response.json();
  70. return { data, status: response.status };
  71. }
  72. // 既存の API 互換性方法を維持、パスとオプションパラメータを受け取り、元の Response を返す
  73. async request(path: string, options: RequestInit = {}): Promise<Response> {
  74. const token = localStorage.getItem('authToken');
  75. const headers = new Headers(options.headers);
  76. if (token) {
  77. headers.set('Authorization', `Bearer ${token}`);
  78. }
  79. const language = localStorage.getItem('userLanguage') || 'ja';
  80. headers.set('x-user-language', language);
  81. // 完全な URL を構築
  82. let url = path;
  83. if (!path.startsWith('http')) {
  84. const cleanPath = path.startsWith('/') ? path : `/${path}`;
  85. url = `${this.baseURL}${cleanPath}`;
  86. }
  87. const response = await fetch(url, {
  88. ...options,
  89. headers,
  90. });
  91. if (response.status === 401) {
  92. localStorage.removeItem('authToken');
  93. window.location.reload();
  94. throw new Error('Unauthorized');
  95. }
  96. return response;
  97. }
  98. }
  99. export const apiClient = new ApiClient(API_BASE_URL);