Safe-Await-Tuple

Simple, powerful Go-style async wrapper for Node.js.

Eliminate messy try/catch blocks and temporary scope variables. Write cleaner, fully-typed asynchronous code from top to bottom.

Designed for Developer Experience

Everything you need to keep your backend code clean, safe, and robust.

Zero Dependencies

Extremely lightweight with zero external bloat. Perfect for serverless functions, edge workers, and high-performance apps.

Strict Type Inference

Built from the ground up for TypeScript. Result types are automatically inferred straight from your promises.

Scope Cleanliness

No more declaring let data; outside scopes just to access it later. Keep your data flow immutable and clean.

Getting Started

Choose your preferred package manager to install safe-await-tuple:

npm
$ npm install safe-await-tuple

Basic Usage

Instead of wrapping your logic in nested try/catch blocks, pass your promise directly to safe():

app.ts
import { safe } from 'safe-await-tuple';

async function handleLogin(req, res) {
  // Returns a tuple: [error, result]
  const [err, user] = await safe(database.findUserByEmail(req.body.email));

  if (err) {
    return res.status(500).json({ error: err.message });
  }

  // user is fully typed and ready
  return res.status(200).json({ success: true, user });
}

Examples

See how safe-await-tuple transforms your daily code architecture across all our current and upcoming versions.

API & Network Requests

Cleanly handle nested async operations without scope pollution.

const [err, response] = await safe(
  fetch('https://api.example.com/data')
);

if (err) {
  console.error('Network request failed:', err.message);
  return fallbackData;
}

const [jsonErr, data] = await safe(response.json());
if (jsonErr) {
  console.error('Failed to parse JSON');
  return null;
}

File System Operations

Perfect for native Node.js async operations.

app.ts
import { promises as fs } from 'fs';

const [err, fileContent] = await safe(fs.readFile('/path/to/config.json', 'utf-8'));

if (err) {
  throw new Error(`Configuration missing: ${err.message}`);
}

const config = JSON.parse(fileContent);

Synchronous Code Execution

Stop crashing your app with faulty JSON.parse(). The safeSync() utility brings tuple error handling to synchronous blocks.

import { safeSync } from 'safe-await-tuple';

function parseConfig(rawJson: string) {
  const [error, config] = safeSync(() => JSON.parse(rawJson));

  if (error) {
    console.error("Invalid JSON:", error.message);
    return null;
  }
  return config;
}

Batch Processing

Bulletproof Promise.all. Run multiple async operations concurrently without fear of one rejection crashing the whole batch.

import { safeAll } from 'safe-await-tuple';

// Returns an array of tuples so you can handle partial successes
const results = await safeAll([
  fetchUser(1),
  fetchUser(2)
]);

const successfulUsers = results
  .filter(([err]) => !err)
  .map(([_, user]) => user);

Custom Error Typing

Go beyond standard Error objects. Pass custom error types via generics so your editor knows exactly what properties are available.

import { safe } from 'safe-await-tuple';

// 'err' is strongly typed as HttpError, not just Error
const [err, data] = await safe(apiCall());

if (err) {
  console.log(err.statusCode); // Typed and auto-completed
}

Global Interceptors

Set up global error interceptors to automatically push caught errors to Sentry, Datadog, or custom logging dashboards.

import { configureSafe } from 'safe-await-tuple';

// Configure once at app startup
configureSafe({
  onError: (err) => Sentry.captureException(err)
});

// Automatically logs to Sentry in the background if it fails
const [err, data] = await safe(riskyOperation());

TypeScript Support

Designed with strict type guards out of the box. No extra configuration needed in your tsconfig.json other than standard strict settings.

"compilerOptions": {
  "target": "ES2022",
  "strict": true
}