UNPKG

create-request

Version:

A modern, chainable wrapper for fetch with automatic retries, timeouts, comprehensive error handling, and first-class TypeScript support

542 lines (541 loc) 19.9 kB
import { GetRequest, PostRequest, PutRequest, DeleteRequest, PatchRequest, HeadRequest, OptionsRequest } from "./requestMethods.js"; import type { RetryConfig, RetryCallback, CookiesRecord, CookieOptions, RequestInterceptor, ResponseInterceptor, ErrorInterceptor } from "./types.js"; import type { CredentialsPolicy, RedirectMode, RequestPriority, ReferrerPolicy, RequestMode } from "./enums.js"; /** * API Builder for creating configured API instances with reusable default settings. */ type ApiBuilder = { withBaseURL(baseURL: string): ApiBuilder; get(path?: string): GetRequest; post(path?: string): PostRequest; put(path?: string): PutRequest; del(path?: string): DeleteRequest; patch(path?: string): PatchRequest; head(path?: string): HeadRequest; options(path?: string): OptionsRequest; /** * Add multiple HTTP headers to the request. * Null and undefined header values are ignored. * * @param headers - An object containing key-value pairs of headers * @returns The API builder instance for chaining * * @example * ```typescript * api.withHeaders({ * 'Content-Type': 'application/json', * 'Authorization': 'Bearer token123' * }); * ``` */ withHeaders(headers: Record<string, string>): ApiBuilder; /** * Add a single HTTP header to the request. * * @param name - The header name * @param value - The header value * @returns The API builder instance for chaining * * @example * ```typescript * api.withHeader('Content-Type', 'application/json'); * ``` */ withHeader(name: string, value: string): ApiBuilder; /** * Set a timeout for the request. * If the request takes longer than the specified time, it will be aborted and a RequestError will be thrown. * * @param timeout - The timeout in milliseconds (must be a positive finite number) * @returns The API builder instance for chaining * @throws {RequestError} If timeout is not a positive finite number * * @example * ```typescript * api.withTimeout(5000); // 5 second timeout * ``` */ withTimeout(timeout: number): ApiBuilder; /** * Configure automatic retry behavior for failed requests. * By default, retries only network errors. For more control, pass a RetryConfig object. * * @param retries - Either: * - A number: number of retry attempts (fixed delay of 1000ms between retries) * - A RetryConfig object with optional properties: * - maxRetries: number of retry attempts * - delay: a function (attempt, error?) => number that returns delay in ms * - retryOn: array of status codes to retry on (in addition to network errors) * - shouldRetry: custom function to decide if a retry should happen * @returns The API builder instance for chaining * * @example * ```typescript * // Simple retry with fixed delay * api.withRetries(3); * ``` * * @example * ```typescript * // Exponential backoff * api.withRetries({ * maxRetries: 3, * delay: (attempt) => Math.pow(2, attempt) * 1000 * }); * ``` * * @example * ```typescript * // Retry specific status codes * api.withRetries({ * maxRetries: 2, * retryOn: [408, 429, 500, 502, 503, 504] * }); * ``` * * @example * ```typescript * // Custom retry logic with error-aware delay * api.withRetries({ * maxRetries: 3, * delay: (attempt, error) => { * if (error?.status === 429) return 5000; // Rate limited * return attempt * 1000; // Linear backoff * }, * shouldRetry: (error) => error.status === 429 || error.status >= 500 * }); * ``` */ withRetries(retries: number | RetryConfig): ApiBuilder; /** * Register a callback to be invoked before each retry attempt. * The callback receives the attempt number (1-indexed), the error that caused the retry, * and the delay before the next retry. * * @param callback - A function that receives (attempt: number, error: RequestError, delay: number) * @returns The API builder instance for chaining * * @example * ```typescript * api.onRetry((attempt, error, delay) => { * console.log(`Retry attempt ${attempt} after ${delay}ms due to:`, error.message); * }); * ``` */ onRetry(callback: RetryCallback): ApiBuilder; /** * Set the credentials policy for the request. * Controls whether cookies and HTTP authentication are sent with cross-origin requests. * * - `include`: Send credentials with both same-origin and cross-origin requests * - `omit`: Never send credentials * - `same-origin` (default): Only send credentials with same-origin requests * * Note: Use direct call pattern. Fluent API (e.g., `.withCredentials.INCLUDE()`) is not supported in ApiBuilder. * * @param credentials - The credentials policy * @returns The API builder instance for chaining * * @example * ```typescript * api.withCredentials('include'); // Send cookies with cross-origin requests * api.withCredentials(CredentialsPolicy.INCLUDE); * ``` */ withCredentials(credentials: CredentialsPolicy): ApiBuilder; /** * Set the referrer URL for the request. * This specifies the referrer to send in the Referer header. * * @param referrer - The referrer URL * @returns The API builder instance for chaining * * @example * ```typescript * api.withReferrer('https://example.com/page'); * ``` */ withReferrer(referrer: string): ApiBuilder; /** * Set the referrer policy for the request. * Controls how much referrer information is included with requests. * * Available policies: * - `no-referrer`: Never send referrer * - `no-referrer-when-downgrade` (default): Send referrer except when going from HTTPS to HTTP * - `origin`: Send only the origin (scheme, host, port) * - `origin-when-cross-origin`: Full URL for same-origin, only origin for cross-origin * - `same-origin`: Send referrer only for same-origin requests * - `strict-origin`: Send origin, but not when going from HTTPS to HTTP * - `strict-origin-when-cross-origin`: Full URL for same-origin, origin for cross-origin HTTPS, nothing for HTTP * - `unsafe-url`: Always send full URL (may leak sensitive information) * * @param policy - The referrer policy * @returns The API builder instance for chaining * * @example * ```typescript * api.withReferrerPolicy('no-referrer'); * api.withReferrerPolicy(ReferrerPolicy.NO_REFERRER); // Using enum * ``` */ withReferrerPolicy(policy: ReferrerPolicy): ApiBuilder; /** * Set the redirect behavior for the request. * * - `follow` (default): Automatically follow redirects * - `error`: Treat redirects as errors * - `manual`: Handle redirects manually (response will have type 'opaqueredirect') * * @param redirect - The redirect mode * @returns The API builder instance for chaining * * @example * ```typescript * api.withRedirect('error'); // Throw an error on redirect * api.withRedirect(RedirectMode.ERROR); // Using enum * ``` */ withRedirect(redirect: RedirectMode): ApiBuilder; /** * Enable or disable HTTP keep-alive for the request. * When enabled, the connection can be reused for multiple requests. * * @param keepalive - Whether to use keep-alive (default is false) * @returns The API builder instance for chaining * * @example * ```typescript * api.withKeepAlive(true); * ``` */ withKeepAlive(keepalive: boolean): ApiBuilder; /** * Set the priority hint for the request. * This provides a hint to the browser about the relative priority of this request. * * - `high`: High priority (e.g., critical resources) * - `low`: Low priority (e.g., prefetch, background tasks) * - `auto` (default): Browser decides the priority * * @param priority - The request priority * @returns The API builder instance for chaining * * @example * ```typescript * api.withPriority('high'); // Mark as high priority * api.withPriority(RequestPriority.HIGH); // Using enum * ``` */ withPriority(priority: RequestPriority): ApiBuilder; /** * Set the Subresource Integrity (SRI) value for the request. * Used to verify that a fetched resource hasn't been tampered with. * * @param integrity - The integrity hash (e.g., 'sha384-...') * @returns The API builder instance for chaining * * @example * ```typescript * api.withIntegrity('sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC'); * ``` */ withIntegrity(integrity: string): ApiBuilder; /** * Set the cache mode for the request. * Controls how the request interacts with the browser's HTTP cache. * * Cache modes: * - `default`: Use the standard HTTP cache behavior (check freshness, use cached response if valid) * - `no-store`: Bypass cache completely, don't store the response * - `reload`: Bypass cache for this request, but store the response * - `no-cache`: Use cached response only after revalidation with the server * - `force-cache`: Use cached response even if stale, only fetch if not cached * - `only-if-cached`: Use cached response or fail (must use with same-origin mode) * * @param cache - The cache mode * @returns The API builder instance for chaining * * @example * ```typescript * api.withCache('no-store'); // Don't cache this request * api.withCache('force-cache'); // Use cache even if stale * ``` */ withCache(cache: RequestCache): ApiBuilder; /** * Set the request mode. * Controls CORS behavior and what types of responses are allowed. * * Request modes: * - `cors` (default): Allow cross-origin requests, follow CORS protocol * - `no-cors`: Make cross-origin request without CORS headers (limited response access) * - `same-origin`: Only allow same-origin requests, reject cross-origin * - `navigate`: Used for navigation requests (usually not needed for fetch) * * @param mode - The request mode * @returns The API builder instance for chaining * * @example * ```typescript * api.withMode('same-origin'); // Only allow same-origin requests * api.withMode(RequestMode.SAME_ORIGIN); // Using enum * ``` */ withMode(mode: RequestMode): ApiBuilder; /** * Sets the Content-Type header for the request. * Shorthand for `withHeader('Content-Type', contentType)`. * * @param contentType - The MIME type (e.g., 'application/json', 'text/plain', 'application/xml') * @returns The API builder instance for chaining * * @example * ```typescript * api.withContentType('application/json'); * ``` * * @example * ```typescript * api.withContentType('application/xml'); * ``` */ withContentType(contentType: string): ApiBuilder; /** * Sets the Authorization header for the request. * Shorthand for `withHeader('Authorization', authValue)`. * For Bearer tokens, use `withBearerToken()` instead. For Basic auth, use `withBasicAuth()`. * * @param authValue - The full authorization header value (e.g., `'Bearer token123'`, `'Basic base64string'`) * @returns The API builder instance for chaining * * @example * ```typescript * api.withAuthorization('Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'); * ``` * * @example * ```typescript * api.withAuthorization('CustomScheme customToken'); * ``` */ withAuthorization(authValue: string): ApiBuilder; /** * Sets up HTTP Basic Authentication. * Encodes the username and password in base64 and sets the Authorization header. * * @param username - The username for Basic authentication * @param password - The password for Basic authentication * @returns The API builder instance for chaining * * @example * ```typescript * api.withBasicAuth('myuser', 'mypassword'); * // Sets: Authorization: Basic bXl1c2VyOm15cGFzc3dvcmQ= * ``` */ withBasicAuth(username: string, password: string): ApiBuilder; /** * Sets a Bearer token for authentication. * Shorthand for `withAuthorization('Bearer ' + token)`. * * @param token - The Bearer token (JWT, OAuth token, etc.) * @returns The API builder instance for chaining * * @example * ```typescript * api.withBearerToken('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'); * // Sets: Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... * ``` */ withBearerToken(token: string): ApiBuilder; /** * Sets cookies for the request. * Cookies are sent in the Cookie header. Multiple calls will merge cookies. * Cookie values can be simple strings or objects with additional cookie options. * * @param cookies - An object where keys are cookie names and values are either: * - A string (the cookie value) * - A CookieOptions object with `value` and optional properties (secure, httpOnly, sameSite, expires, path, domain, maxAge) * @returns The API builder instance for chaining * * @example * ```typescript * // Simple string cookies * api.withCookies({ sessionId: 'abc123', userId: '456' }); * ``` * * @example * ```typescript * // Cookies with options (note: options are for documentation only in request cookies) * api.withCookies({ * sessionId: 'abc123', * token: { value: 'xyz789', secure: true } * }); * ``` */ withCookies(cookies: CookiesRecord): ApiBuilder; /** * Sets a single cookie for the request. * Convenience method for adding one cookie at a time. * * @param name - The cookie name * @param value - The cookie value as a string, or a CookieOptions object with `value` and optional properties * @returns The API builder instance for chaining * * @example * ```typescript * api.withCookie('sessionId', 'abc123'); * ``` * * @example * ```typescript * api.withCookie('token', { value: 'xyz789', secure: true }); * ``` */ withCookie(name: string, value: string | CookieOptions): ApiBuilder; /** * Sets a CSRF (Cross-Site Request Forgery) token in the request headers. * This is commonly used to protect against CSRF attacks in web applications. * * @param token - The CSRF token value * @param headerName - The name of the header to use. Defaults to `'X-CSRF-Token'`. * @returns The API builder instance for chaining * * @example * ```typescript * api.withCsrfToken('csrf-token-123'); * // Sets: X-CSRF-Token: csrf-token-123 * ``` * * @example * ```typescript * api.withCsrfToken('token', 'X-Custom-CSRF-Header'); * // Sets: X-Custom-CSRF-Header: token * ``` */ withCsrfToken(token: string, headerName?: string): ApiBuilder; /** * Disables automatic anti-CSRF protection. * By default, X-Requested-With: XMLHttpRequest header is sent with all requests. * @returns The API builder instance for chaining */ withoutCsrfProtection(): ApiBuilder; /** * Sets common security headers to help prevent CSRF attacks * @returns The API builder instance for chaining */ withAntiCsrfHeaders(): ApiBuilder; /** * Add a request interceptor for this specific request * Request interceptors can modify the request configuration or return an early response * * @param interceptor - The request interceptor function * @returns The API builder instance for chaining * * @example * api.withRequestInterceptor((config) => { * config.headers['X-Custom'] = 'value'; * return config; * }); */ withRequestInterceptor(interceptor: RequestInterceptor): ApiBuilder; /** * Add a response interceptor for this specific request * Response interceptors can transform the response * * @param interceptor - The response interceptor function * @returns The API builder instance for chaining * * @example * api.withResponseInterceptor((response) => { * console.log('Status:', response.status); * return response; * }); */ withResponseInterceptor(interceptor: ResponseInterceptor): ApiBuilder; /** * Add an error interceptor for this specific request * Error interceptors can handle or transform errors * * @param interceptor - The error interceptor function * @returns The API builder instance for chaining * * @example * api.withErrorInterceptor((error) => { * console.error('Request failed:', error); * throw error; * }); */ withErrorInterceptor(interceptor: ErrorInterceptor): ApiBuilder; /** * Adds default query parameters to all requests made through this API instance. * * @param params - An object containing query parameter key-value pairs * @returns The API builder instance for chaining * * @example * ```typescript * const api = createApi() * .withBaseURL('https://api.example.com') * .withQueryParams({ apiVersion: 'v2', format: 'json' }); * // All requests will include ?apiVersion=v2&format=json * ``` */ withQueryParams(params: Record<string, string | string[] | number | boolean | null | undefined>): ApiBuilder; /** * Adds a single default query parameter to all requests made through this API instance. * * @param key - The query parameter name * @param value - The query parameter value * @returns The API builder instance for chaining * * @example * ```typescript * const api = createApi() * .withBaseURL('https://api.example.com') * .withQueryParam('apiKey', 'abc123'); * ``` */ withQueryParam(key: string, value: string | string[] | number | boolean | null | undefined): ApiBuilder; }; /** * Creates a new API builder for configuring default request settings. * The API builder allows you to set up a base URL, default headers, timeout, * and other configuration options that will be applied to all requests made through it. * * @returns A new API builder instance * * @example * ```typescript * // Create an API instance with defaults * const api = api() * .withBaseURL('https://api.example.com') * .withBearerToken('token123') * .withTimeout(5000); * * // All requests will use these defaults * const users = await api.get('/users').getJson(); * const newUser = await api.post('/users').withBody({ name: 'John' }).getJson(); * ``` * * @example * ```typescript * // Use without URL when baseURL is set * const api = api().withBaseURL('https://api.example.com'); * const data = await api.get().getJson(); // Requests to https://api.example.com * ``` * * @example * ```typescript * // Override defaults per request * const api = api() * .withBaseURL('https://api.example.com') * .withTimeout(5000); * * // This request uses a longer timeout * await api.get('/slow-endpoint').withTimeout(30000).getJson(); * ``` */ export declare function api(): ApiBuilder; export {};