UNPKG

create-nttb

Version:

An opinionated Next.js, TypeScript, and Tailwind boilerplate using Atomic Design Methodology for presentation components.

57 lines (50 loc) 1.82 kB
import { NEXT_PUBLIC_API_URL } from "../config/variables"; /** * Represents an HTTP error returned from a failed request. */ export class HttpError extends Error { public readonly status: number | undefined; constructor(status: number | undefined, message: string) { super(message); this.name = "HttpError"; this.status = status; } } /** * Makes an HTTP request using the native fetch API. * * Automatically prepends the base API URL from environment config, * sets JSON content headers, and throws an {@link HttpError} for * non-2xx responses with the status code and error message from the body. * * Requests time out after 5 seconds via {@link AbortSignal.timeout}. * * @param path - The API path to append to the base URL (e.g. `/users/1`). * @param options - Standard {@link RequestInit} fetch options (method, body, headers, etc). * @returns The parsed JSON response body typed as `T`. * @throws {@link HttpError} if the response status is not ok, or if the network fails. * @throws {@link DOMException} with name `"TimeoutError"` if the request exceeds 5 seconds. * * @example * const user = await request<User>("/users/1"); * const created = await request<User>("/users", { method: "POST", body: JSON.stringify(data) }); */ export async function request<T = unknown>( path: string, options?: RequestInit, ): Promise<T> { const url = `${NEXT_PUBLIC_API_URL}${path}`; const response = await fetch(url, { signal: AbortSignal.timeout(5000), ...options, headers: { "Content-Type": "application/json", ...options?.headers, }, }); if (!response.ok) { const body = await response.json().catch(() => ({})); throw new HttpError(response.status, body?.message ?? response.statusText); } return response.json() as Promise<T>; }