Files
calchat/apps/client/src/services/ApiClient.ts
Linus Waldowsky 71f84d1cc7 feat: implement structured logging for server and client
Server:
- Add pino and pino-http for structured logging
- Create @Logged class decorator using Proxy pattern for automatic method logging
- Add pino redact config for sensitive data (password, token, etc.)
- Move AuthMiddleware to controllers folder (per architecture diagram)
- Add LoggingMiddleware for HTTP request logging
- Replace console.log/error with structured logger in controllers and app.ts
- Decorate all repositories and GPTAdapter with @Logged

Client:
- Add react-native-logs with namespaced loggers (apiLogger, storeLogger)
- Add request/response logging to ApiClient with duration tracking
2026-01-10 16:59:40 +01:00

85 lines
2.1 KiB
TypeScript

import { Platform } from "react-native";
import { apiLogger } from "../logging";
const API_BASE_URL =
process.env.EXPO_PUBLIC_API_URL ||
Platform.select({
android: "http://10.0.2.2:3001/api",
default: "http://localhost:3001/api",
});
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
interface RequestOptions {
headers?: Record<string, string>;
body?: unknown;
}
async function request<T>(
method: HttpMethod,
endpoint: string,
options?: RequestOptions,
): Promise<T> {
const start = performance.now();
apiLogger.debug(`${method} ${endpoint}`);
try {
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
method,
headers: {
"Content-Type": "application/json",
...options?.headers,
},
body: options?.body ? JSON.stringify(options.body) : undefined,
});
const duration = Math.round(performance.now() - start);
if (!response.ok) {
apiLogger.error(`${method} ${endpoint} - ${response.status} (${duration}ms)`);
throw new Error(`HTTP ${response.status}`);
}
apiLogger.debug(`${method} ${endpoint} - ${response.status} (${duration}ms)`);
return response.json();
} catch (error) {
const duration = Math.round(performance.now() - start);
apiLogger.error(`${method} ${endpoint} failed (${duration}ms): ${error}`);
throw error;
}
}
export const ApiClient = {
get: async <T>(
endpoint: string,
options?: Omit<RequestOptions, "body">,
): Promise<T> => {
return request<T>("GET", endpoint, options);
},
post: async <T>(
endpoint: string,
body?: unknown,
options?: RequestOptions,
): Promise<T> => {
return request<T>("POST", endpoint, { ...options, body });
},
put: async <T>(
endpoint: string,
body?: unknown,
options?: RequestOptions,
): Promise<T> => {
return request<T>("PUT", endpoint, { ...options, body });
},
delete: async <T>(
endpoint: string,
options?: Omit<RequestOptions, "body">,
): Promise<T> => {
return request<T>("DELETE", endpoint, options);
},
};
export { API_BASE_URL };