Skip to content

Latest commit

 

History

History
114 lines (85 loc) · 2.26 KB

File metadata and controls

114 lines (85 loc) · 2.26 KB

TypeScript

FetchResponse<T, E> is a discriminated union keyed by ok.

Narrowing

interface User {
  id: number;
  name: string;
}

interface ApiError {
  code: string;
  message: string;
}

const response = await api.get<User, ApiError>('/users/1');

if (response.ok) {
  response.data.name; // User
  response.error; // null
} else {
  response.data; // null
  response.error.body?.code; // ApiError | undefined
}

Common Types

import type {
  FetchClientOptions,
  FetchFailureResponse,
  FetchResponse,
  FetchResponseError,
  FetchSuccessResponse,
  InterceptMiddleware,
  RequestOptions,
} from '@fgrzl/fetch';

Use the second generic to type parsed error bodies:

type UserResponse = FetchResponse<User, ApiError>;

FetchClientOptions shapes the client constructor and RequestOptions shapes per-request overrides.

const clientOptions: FetchClientOptions = {
  baseUrl: 'https://api.example.com',
  credentials: 'same-origin',
  timeout: 5000,
};

const requestOptions: RequestOptions = {
  timeout: 1000,
  operationId: 'trace-123',
};

Client And Middleware Types

import type { AuthenticationOptions } from '@fgrzl/fetch/middleware/authentication';
import type { RetryOptions } from '@fgrzl/fetch/middleware/retry';

const retry: RetryOptions = {
  maxRetries: 3,
  delay: 250,
  backoff: 'exponential',
};
const traceMiddleware: InterceptMiddleware = async (request, next) => {
  const headers = new Headers(request.headers);
  headers.set('X-Trace-Id', crypto.randomUUID());

  return next({ ...request, headers });
};

Optional Exceptions

import { FetchClient, throwOnError } from '@fgrzl/fetch';

const api = new FetchClient();

const user: User = throwOnError(await api.get<User>('/users/1'));

throwOnError keeps response-based calls as the default while supporting integrations that expect exceptions.

Endpoint Wrappers

import { FetchClient } from '@fgrzl/fetch';

const api = new FetchClient();

class UsersApi {
  constructor(private readonly client = api) {}

  async getUser(id: string): Promise<FetchResponse<User, ApiError>> {
    return this.client.get<User, ApiError>(`/users/${id}`);
  }
}

Prefer small endpoint wrappers when you want named operations and shared response types.