Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

client.ts 2.3 KiB

3 semanas atrás
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. export class ApiError extends Error {
  2. readonly status: number;
  3. readonly code: string;
  4. readonly traceId?: string;
  5. constructor(status: number, code: string, message: string, traceId?: string) {
  6. super(message);
  7. this.name = "ApiError";
  8. this.status = status;
  9. this.code = code;
  10. this.traceId = traceId;
  11. }
  12. }
  13. type RequestOptions = {
  14. method: string;
  15. path: string;
  16. query?: Record<string, string | number | boolean | undefined>;
  17. body?: unknown;
  18. expectedStatus: number;
  19. };
  20. function buildUrl(path: string, query?: RequestOptions["query"]): string {
  21. const url = new URL(path, window.location.origin);
  22. if (query) {
  23. for (const [key, value] of Object.entries(query)) {
  24. if (value !== undefined && value !== "") {
  25. url.searchParams.set(key, String(value));
  26. }
  27. }
  28. }
  29. return `${url.pathname}${url.search}`;
  30. }
  31. export async function request<T>(options: RequestOptions): Promise<T> {
  32. const headers: Record<string, string> = { Accept: "application/json" };
  33. if (options.body !== undefined) {
  34. headers["Content-Type"] = "application/json";
  35. }
  36. const response = await fetch(buildUrl(options.path, options.query), {
  37. method: options.method,
  38. headers,
  39. body: options.body === undefined ? undefined : JSON.stringify(options.body),
  40. });
  41. if (response.status === 204 || options.expectedStatus === 204) {
  42. if (!response.ok && response.status !== options.expectedStatus) {
  43. throw await toApiError(response);
  44. }
  45. return undefined as T;
  46. }
  47. if (response.status !== options.expectedStatus) {
  48. throw await toApiError(response);
  49. }
  50. if (response.headers.get("content-type")?.includes("application/json")) {
  51. return (await response.json()) as T;
  52. }
  53. return undefined as T;
  54. }
  55. async function toApiError(response: Response): Promise<ApiError> {
  56. const traceId = response.headers.get("x-request-id") ?? undefined;
  57. try {
  58. const payload = (await response.json()) as {
  59. error?: { code?: string; message?: string; trace_id?: string };
  60. };
  61. return new ApiError(
  62. response.status,
  63. payload.error?.code ?? "http_error",
  64. payload.error?.message ?? response.statusText,
  65. payload.error?.trace_id ?? traceId,
  66. );
  67. } catch {
  68. return new ApiError(response.status, "http_error", response.statusText, traceId);
  69. }
  70. }