A TypeScript library for parsing fetch responses with support for multiple content types.
- 🚀 Strategy Pattern: Automatically selects the right parser based on content type
- 📦 TypeScript First: Full type safety with discriminated unions and generics
- 🎯 Comprehensive: Supports JSON, XML, text, binary, and more content types
- ✅ Type-Safe Results: Discriminated union types for perfect type safety
- 🔗 Chainable API: Clean chaining with
withParseandwithSafeParse
npm install parse-fetchimport {
parseFetch,
safeParseFetch,
withParse,
withSafeParse,
} from 'parse-fetch';
// Option 1: withParse higher-order function (recommended for throwing errors)
const parseFetch = withParse(fetch);
const data = await parseFetch(
'https://api.example.com/data'
).parse<ApiResponse>();
// Option 2: Direct throwing function call
const response = await fetch('https://api.example.com/data');
const data = await parseFetch<ApiResponse>(response);
// Option 3: Safe versions that return discriminated union results
const parseFetchSafe = withSafeParse(fetch);
const result = await parseFetchSafe(
'https://api.example.com/data'
).parse<ApiResponse>();
if (result.success) {
console.log('Data:', result.data);
} else {
// result.errors is BaseApiError[]
console.error('Errors:', result.errors.map(e => e.message).join(', '));
}
// Option 4: Direct safe function call
const response = await fetch('https://api.example.com/data');
const result = await safeParseFetch<ApiResponse>(response);
if (result.success) {
console.log('Data:', result.data);
} else {
console.error('Errors:', result.errors.map(e => e.message).join(', '));
}
// With options
const parseFetch = withParse(fetch);
const data = await parseFetch('https://api.example.com/data', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
}).parse<ApiResponse>();The library uses discriminated union types for perfect type safety:
type BaseApiError =
| { kind: 'parse' | 'network'; message: string; originalError?: unknown }
| {
kind: 'http';
message: string;
status: number;
statusText: string;
bodyText?: string;
};
type ParseResult<T> =
| { success: true; data: T }
| { success: false; errors: BaseApiError[] };const result = await safeParseFetch<ApiResponse>(response);
if (result.success) {
// TypeScript knows result.data exists and is of type ApiResponse
console.log(result.data.message); // ✅ Type-safe access
// console.log(result.errors); // ❌ TypeScript error - errors doesn't exist on success
} else {
// TypeScript knows result.errors exists and is BaseApiError[]
for (const err of result.errors) {
if (err.kind === 'http') {
console.error(`[HTTP ${err.status} ${err.statusText}] ${err.message}`);
} else {
console.error(err.message);
}
}
// console.log(result.data); // ❌ data doesn't exist on failure
}The library provides two approaches for handling errors:
import { parseFetch, withParse } from 'parse-fetch';
try {
const parseFetch = withParse(fetch);
const data = await parseFetch(
'https://api.example.com/data'
).parse<ApiResponse>();
console.log('Success:', data);
} catch (error) {
console.error('Error:', error.message);
}import { withSafeParse } from 'parse-fetch';
const parseFetchSafe = withSafeParse(fetch);
const result = await parseFetchSafe(
'https://api.example.com/data'
).parse<ApiResponse>();
if (result.success) {
console.log('Success:', result.data);
} else {
console.error('Errors:', result.errors.map(e => e.message).join(', '));
}Choose the approach that fits your error handling style:
- Throwing: Use try/catch blocks, good for centralized error handling
- Result objects: Use if/else checks, good for explicit error handling and functional programming
import { withParse } from 'parse-fetch';
// Create enhanced fetch
const parseFetch = withParse(fetch);
// Clean chaining syntax
const data = await parseFetch(
'https://api.example.com/data'
).parse<ApiResponse>();
// Preserves all Response properties
const response = await parseFetch('https://api.example.com/data');
console.log(response.status); // 200
console.log(response.ok); // trueimport { parseFetch } from 'parse-fetch';
// Traditional approach
const response = await fetch('https://api.example.com/data');
const data = await parseFetch<ApiResponse>(response);application/jsonapplication/ld+json(JSON-LD)application/vnd.api+json(JSON API)
application/xmltext/xmlapplication/atom+xml(Atom feeds)application/rss+xml(RSS feeds)application/soap+xml(SOAP)
text/plaintext/htmltext/csstext/csvtext/javascriptapplication/javascriptapplication/x-www-form-urlencodedmultipart/form-data
application/octet-streamapplication/pdfimage/*(PNG, JPEG, GIF, SVG, WebP)video/*(MP4, WebM, AVI)audio/*(MP3, WAV, OGG)application/zipapplication/x-tarapplication/gzip
Parses a fetch response based on its content type.
Parameters:
response: Response- The fetch response objectoptions: ParseOptions- Optional configuration
Returns: Promise<T> - The parsed data
interface ParseOptions {
contentType?: KnownContentType; // Override content type detection
reviver?: (key: string, value: any) => any; // JSON reviver
}Returns a discriminated union ParseResult<T> where failures contain structured errors:
type BaseApiError =
| { kind: 'parse' | 'network'; message: string; originalError?: unknown }
| {
kind: 'http';
message: string;
status: number;
statusText: string;
bodyText?: string;
};
type ParseResult<T> =
| { success: true; data: T }
| { success: false; errors: BaseApiError[] };ISC