Typescript
Classe HTTP para interagir com API
Classe HTTP para abstrair a interação com APIs no frontend, já resolve o endpoint da api, extrai os dados, se retornou erro extrai a message de erro
http.tsx
interface FetchOptions extends RequestInit {
params?: Record<string, string | undefined | null | number>;
formData?: boolean;
}
class Http {
private baseURL: string;
constructor(baseURL: string) {
this.baseURL = baseURL;
}
private async request<T>(path: string, options: FetchOptions = {}): Promise<T> {
//const url = new URL(path, this.baseURL);
let urlString: string;
if (path.startsWith("http")) {
urlString = path;
} else {
// Garante que o path tenha barra inicial
const safePath = path.startsWith("/") ? path : \`/\${path}\`;
// Garante que a baseURL não tenha barra final para não duplicar
const safeBase = this.baseURL.replace(/\\/\$/, "");
// Junta as strings manualmente: .../api + /auth/sign-in
urlString = \`\${safeBase}\${safePath}\`;
}
const url = new URL(urlString);
if (options.params) {
for (const [key, value] of Object.entries(options.params)) {
if (value) {
url.searchParams.append(key, String(value));
}
}
}
const headers = {
...options.headers,
...(!options.formData
? {
"Content-Type": "application/json",
}
: {}),
};
const response = await fetch(url, {
...options,
headers,
credentials: "include",
});
let data: any = null;
try {
data = await response.json();
} catch (error) {
data = null;
}
console.log(\`[HTTP] \${response.status} \${url.pathname}\`, data);
if (!response.ok) {
let errorMessage: string;
if (data && typeof data === "object") {
// 1. Tenta pegar propriedades comuns de erro
const rawMessage = data.message || data.error || data.errors;
if (typeof rawMessage === "string") {
// Se for uma string simples, usa ela
errorMessage = rawMessage;
} else if (rawMessage) {
// Se message/error for um objeto/array (ex: lista de validações), converte para JSON
errorMessage = JSON.stringify(rawMessage);
} else {
// Se não tiver propriedades conhecidas, converte o objeto inteiro
errorMessage = JSON.stringify(data);
}
} else if (typeof data === "string") {
// Se a resposta for texto puro
errorMessage = data;
} else {
// Fallback genérico
errorMessage = \`Erro \${response.status}: Ocorreu um erro desconhecido na requisição.\`;
}
throw new Error(errorMessage);
}
return data as T;
}
async get<T>(path: string, options?: FetchOptions): Promise<T> {
return this.request<T>(path, { ...options, method: "GET" });
}
async post<T>(path: string, data?: unknown, options: FetchOptions = {}): Promise<T> {
const isFormData = options.formData;
return this.request<T>(path, {
...options,
method: "POST",
body: isFormData ? (data as BodyInit) : JSON.stringify(data),
});
}
async put<T>(path: string, data?: unknown, options: FetchOptions = {}): Promise<T> {
const isFormData = options.formData;
return this.request<T>(path, {
...options,
method: "PUT",
body: isFormData ? (data as BodyInit) : JSON.stringify(data),
});
}
async patch<T>(path: string, data?: unknown, options?: RequestInit): Promise<T> {
return this.request<T>(path, {
...options,
method: "PATCH",
body: JSON.stringify(data),
});
}
async delete<T>(path: string, options?: RequestInit): Promise<T> {
return this.request<T>(path, { ...options, method: "DELETE" });
}
}
export const http = new Http(process.env.NEXT_PUBLIC_API_URL || "");
Ver Detalhes