Skip to content

Basic Usage

Simple GET Request

typescript
import { typedFetch, NotFoundError, UnauthorizedError } from '@pbpeterson/typed-fetch';

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

// Specify expected errors for better type safety
type ExpectedErrors = NotFoundError | UnauthorizedError;
const { response, error } = await typedFetch<User[], ExpectedErrors>('/api/users');

if (error) {
  if (error instanceof NotFoundError) {
    console.log('Users endpoint not found');
  } else if (error instanceof UnauthorizedError) {
    console.log('Authentication required');
  } else {
    console.error('Unexpected error:', error.statusText);
  }
} else {
  const users = await response.json(); // Type: User[]
  console.log('Users:', users);
}

Specifying Expected Client Errors

⚠️ IMPORTANT: It's highly recommended to specify which client errors (4xx) you expect from your API. When you don't specify the second generic parameter, the library will include ALL possible client errors in the error union type, making error handling more complex.

typescript
import { typedFetch, NotFoundError, UnauthorizedError } from '@pbpeterson/typed-fetch';

// ❌ Without specifying expected errors - includes ALL client errors
const { response, error } = await typedFetch<User[]>('/api/users');
// error type: BadRequestError | PaymentRequiredError | UnauthorizedError | ... (30+ error types) | ServerErrors | NetworkError | null

// ✅ Specifying only expected client errors - much cleaner
type ExpectedErrors = NotFoundError | UnauthorizedError;
const { response: betterResponse, error: betterError } = await typedFetch<User[], ExpectedErrors>('/api/users');
// betterError type: NotFoundError | UnauthorizedError | ServerErrors | NetworkError | null

if (betterError) {
  if (betterError instanceof NotFoundError) {
    console.log('Users not found');
  } else if (betterError instanceof UnauthorizedError) {
    console.log('Please authenticate');
  } else {
    // Server errors (5xx) or Network errors - usually unexpected
    console.error('Unexpected error:', betterError.statusText);
  }
}

Why we always include Server Errors and Network Errors:

  • Server Errors (5xx): These are unexpected and cannot be controlled by the client
  • Network Errors: Connection issues, timeouts, and DNS failures are always possible
  • These error types help you handle unexpected situations gracefully

POST Request with Body

typescript
import { typedFetch, BadRequestError } from '@pbpeterson/typed-fetch';

const newUser = { name: 'John Doe', email: 'john@example.com' };

// Specify BadRequestError as expected client error for POST validation
const { response, error } = await typedFetch<User, BadRequestError>('/api/users', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify(newUser),
});

if (error) {
  if (error instanceof BadRequestError) {
    const validationErrors = await error.json();
    console.error('Validation failed:', validationErrors);
  } else {
    console.error('Request failed:', error.statusText);
  }
} else {
  const user = await response.json(); // Type: User
  console.log('Created user:', user);
}

Error Response Cloning

Handle error responses that need to be read multiple times:

typescript
import { typedFetch, UnauthorizedError, NotFoundError } from '@pbpeterson/typed-fetch';

type ExpectedErrors = UnauthorizedError | NotFoundError;
const { error } = await typedFetch<User[], ExpectedErrors>('/api/users');

if (error && 'clone' in error) {
  // Clone the error to read response body multiple times
  const clonedError = error.clone();
  
  if (error instanceof UnauthorizedError) {
    console.log('Authentication required');
  } else if (error instanceof NotFoundError) {
    console.log('Users not found');
  }
  
  // Log the error details
  const errorDetails = await error.json();
  console.error('Error details:', errorDetails);
  
  // Send to monitoring service
  const monitoringData = await clonedError.json();
  analytics.track('api_error', monitoringData);
}

Request Cancellation and Timeout

Since typedFetch uses the same API as native fetch, all modern features work:

typescript
import { typedFetch, UnauthorizedError, NotFoundError } from '@pbpeterson/typed-fetch';

// Request cancellation
const controller = new AbortController();

type ExpectedErrors = UnauthorizedError | NotFoundError;
const { response, error } = await typedFetch<User[], ExpectedErrors>('/api/users', {
  signal: controller.signal
});

// Cancel after 2 seconds
setTimeout(() => controller.abort(), 2000);

// Request timeout (modern browsers)
const { response: timeoutResponse, error: timeoutError } = await typedFetch<User[], ExpectedErrors>('/api/users', {
  signal: AbortSignal.timeout(5000) // 5 second timeout
});

Released under the MIT License.