npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

try-fetch-catch

v1.0.2

Published

Go-style error handling for JavaScript that never throws. tryFetch returns [error, data, response] tuples, tryCatch returns [error, result] tuples.

Downloads

184

Readme

npm version npm downloads

try-fetch-catch

Go-style error handling for JavaScript/TypeScript.

try-fetch-catch provides two small utilities that replace thrown exceptions with predictable tuples:

  • tryFetch wraps fetch() and returns [error, data, response].
  • tryCatch wraps any sync/async function and returns [error, result].

No thrown exceptions. No nested try/catch. No unhandled promise rejections. Just linear, easy-to-read code.

Install

npm install try-fetch-catch

Requirements:

  • Node.js 18+ (native fetch)
  • Or any runtime that provides the Fetch API (modern browsers, Deno, Bun)

Import in your project:

import { tryFetch, tryCatch } from "try-fetch-catch";
// OR
const { tryFetch, tryCatch } = require("try-fetch-catch");

Note: This package ships native ESM and CommonJS builds.

Why use try-fetch-catch

  • 🎯 Clean code - Linear, no-throw control flow without nested try/catch boilerplate.
  • 🔌 Drop-in fetch replacement - Replace fetch with tryFetch and access err + data on the same line.
  • ⚡ Smart auto-parsing — Parses by the Response Content-Type header automatically, with easy overrides or a custom parser when you need full control.
  • 🚫 No uncaught exceptions — Thrown exceptions are captured internally and returned as typed error values.
  • 💪 TypeScript-friendly - Generics and exported types for predictable, typed results.
  • 📦 Tiny & dependency-free - Minimal footprint, zero dependencies, low maintenance.

Example comparison

  1. Fetching Data
// BEFORE (Traditional fetch)
try {
  const response = await fetch("/api/users/123");
  if (!response.ok) throw new Error("HTTP Error");
  const data = await response.json();
} catch (err) {
  console.error(err);
}

// AFTER (tryFetch)
const [err, data] = await tryFetch("/api/users/123");
if (err) console.error(err);

Key advantages: Linear, automatic HTTP & parse handling, fully predictable.

  1. Parsing Data
// BEFORE (Traditional JSON.parse)
try {
  const user = JSON.parse(userDataString);
} catch (err) {
  console.error(err);
}

// AFTER (tryCatch)
const [err, user] = tryCatch(JSON.parse, userDataString);
if (err) console.error(err);

Key advantages: Works for sync/async, linear flow, no try/catch boilerplate.

Using tryFetch

tryFetch is a drop-in replacement for native fetch that never throws.

Instead of relying on thrown exceptions, it always returns a tuple where errors, data, and the response are explicit and predictable.

Core rule: tryFetch never throws. All failures are returned as data.

Basic Usage

Use tryFetch exactly like fetch, but destructure the result instead of wrapping it in try/catch.

import { tryFetch } from "try-fetch-catch";

const [err, user] = await tryFetch<User>(`/api/users/${id}`);

if (err) {
  // handle error
  return;
}

// `user` is safe and already parsed
renderUser(user);

This pattern keeps control flow linear and makes error handling impossible to forget - a common source of bugs in the JavaScript world.

Error model

tryFetch always resolves to a tuple with one of the following shapes:

  • Success
    [null, data, Response]

  • HTTP error (non-2xx response)
    [HttpError, data | null, Response]
    (data is null if the response body can’t be parsed)

  • Network / transport error (no response)
    [NetworkError, null, undefined]

  • Response parse error
    [ParseError, null, Response]
    (request succeeded, but body parsing failed)

A common error handling pattern:

const [err, data, res] = await tryFetch<User>(`/api/users/${id}`);

if (err?.status === 0) {
  // Network error: no data and HTTP response
  return showOfflineUI(err.message);
}

if (err) {
  // HTTP error (4xx / 5xx)
  return showErrorPage(err.status);
}

// Success - data is safe
renderUser(data);

Best practice: Always check err before using data.

Signature

tryFetch<T = unknown>(
  input: RequestInfo | URL,
  init?: RequestInit & { tryFetchOptions?: TryFetchOptions<T> },
): Promise<FetchResult<T>>

Automatic response parsing

By default, tryFetch automatically parses the response body based on the Content-Type header:

  • JSON (application/json, +json) → parsed JSON
  • Text (text/\*, XML types) → response.text()
  • Binary (image/\*, application/octet-stream, application/pdf) → response.blob()
  • Form data (multipart/form-data, application/x-www-form-urlencoded) → response.formData()
  • Unknown or missing type → reads text, then attempts a best-effort JSON.parse
  • Empty bodies (204/205, content-length: 0, or empty JSON) → null

In most cases, you never need to think about parsing — the returned data is already usable.

Note: On HTTP error responses, tryFetch still attempts to parse the response body so APIs can return structured error payloads.

Overriding the parser

Auto-parsing covers most APIs. When you need more control, you have two options.

Force a specific parse mode

const [err, text] = await tryFetch<string>("/api/raw", {
  tryFetchOptions: { parseAs: "text" },
});

Accepted values for parseAs: "json" | "text" | "blob" | "arrayBuffer" | "formData"

Provide a custom parser

const [err, upper] = await tryFetch<string>("/api/greeting", {
  tryFetchOptions: {
    parser: async (response) => (await response.text()).toUpperCase(),
  },
});

If a custom parser throws, the error is returned as a ParseError.

Response consumption and preserveResponse

By default, tryFetch consumes the response body once while parsing it.

  • Network error returns no Response.
  • HTTP responses (success or error) return a Response whose body is already consumed.
  • You can still safely access metadata like status, statusText, and headers.

If you need to read the response body more than once, enable preserveResponse:

const [err, data, res] = await tryFetch("/api/data", {
  tryFetchOptions: { preserveResponse: true },
});

if (!err) {
  const again = await res.json(); // body is still readable
}

When preserveResponse is enabled, parsing is performed on a cloned Response so the returned Response remains readable.

tryFetchOptions reference

tryFetchOptions is passed inside the native fetch init object:

await tryFetch(url, {
  ...RequestInit,
  tryFetchOptions: {
    /* ... */
  },
});

| Option | Type | Accepted values | Default | Usage | | ------------------ | ----------------------------------------- | ------------------------------------------------------------------- | ------------------------------ | ---------------------------------------------- | | parseAs | ResponseParseAs | "json" | "text" | "blob" | "arrayBuffer" | "formData" | Auto-detect via Content-Type | tryFetchOptions: { parseAs: "text" } | | parser | (response: Response) => T \| Promise<T> | Any function | none | tryFetchOptions: { parser: (r) => r.text() } | | preserveResponse | boolean | true / false | false | tryFetchOptions: { preserveResponse: true } |

Notes:

  • parser overrides parseAs and Content-Type auto-parsing.
  • preserveResponse: true ensures the returned Response remains readable.

Using tryCatch

tryCatch wraps any synchronous or asynchronous function and returns [error, result] instead of throwing.

It always resolves to a predictable tuple:

  • Success: [null, result]
  • Failure: [Error, null]

Non-Error throws are coerced to Error type.

Examples

Asynchronous function:

// Direct function + arguments
const [err, user] = await tryCatch(loadUser, userId);

// OR using a callback
const [err2, user2] = await tryCatch(() => loadUser(userId));

if (err) return log.error(err.message);

log.info(user);

Synchronous function:

// Direct function + arguments
const [err, user] = tryCatch(JSON.parse, userDataString);

// OR using a callback
const [err2, user2] = tryCatch(() => JSON.parse(userDataString));

if (err) return;

log.info(user);

Types

This package exports a small set of TypeScript types for tryFetch.

import type {
  FetchResult,
  HttpError,
  NetworkError,
  ParseError,
  TryFetchOptions,
  TryFetchRequestInit,
  ResponseParseAs,
} from "try-fetch-catch";

FetchResult<T>

tryFetch always resolves to one of these tuple shapes:

  • Success: [null, T, Response]
  • HTTP error (non-2xx): [HttpError, T | null, Response]
  • Parse error (2xx but body parsing failed): [ParseError, null, Response]
  • Network/transport error (no Response): [NetworkError, null, undefined]

TryFetchOptions<T> and ResponseParseAs

Use TryFetchOptions<T> under init.tryFetchOptions to force parsing (parseAs) or provide a custom parser.

TryFetchRequestInit<T>

Convenience type for the init parameter: native RequestInit plus tryFetchOptions.

tryCatch typing

tryCatch is intentionally minimal: it returns [err, result].

  • Success: [null, T]
  • Failure: [Error, null]

Note: result can still be null on success if the wrapped function can return null (i.e. your T includes null).

Author

Sion Young

License

ISC