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

@heroku/heroku-fetch

v0.1.1-beta.0

Published

A JavaScript/TypeScript API client for Heroku APIs

Downloads

170

Maintainers

srishtigautamsrishtigautamaklimaiteaklimaitejsullivandigsjsullivandigsjulianduque-sfdcjulianduque-sfdccwallsfcwallsfijanakijanakjeffrey.estradajeffrey.estradadlira-sfdlira-sfntonollierntonollierdaebumleedaebumleemttrmttrelearyelearysalesforce-releasessalesforce-releasesanujpandey001anujpandey001kbaird-sfkbaird-sfcbaxtersfcbaxtersfryoneyamaryoneyamaadirasanamadirasanamnrisaronrisarocmarcojoscmarcojosmgates-salesforcemgates-salesforceademusoyoademusoyoelzoonaelzoonadhagberg-sfdhagberg-sfbencdentonbencdentonmarcelsfdcmarcelsfdcccaseyccaseysuppadasuppadadiana.doherty.sfdcdiana.doherty.sfdchritchhritchrsoerensenrsoerensenjasoncummings-herokujasoncummings-herokubrock-salesforcebrock-salesforcetkoh-sfdctkoh-sfdcgnettergnettercoreypurcell_salesforcecoreypurcell_salesforceswapnilghoseswapnilghosestessema-sfcstessema-sfcbrittany.jonesbrittany.jonesrstiltonsfrstiltonsfnilamuthunilamuthusghaffarsghaffarcdwort-sfdccdwort-sfdcaaronromeosfdcaaronromeosfdckpremkumarkpremkumarrchandarchandaxx-sfdc-npmxx-sfdc-npmcv88cv88ekozilforceekozilforceumarsfdcumarsfdcndavidson-sfndavidson-sfmingzhi-liu1258mingzhi-liu1258angelcampbellangelcampbellmandeepsfmandeepsfyann_ckyann_cksudarshanhiraysudarshanhirayalex_herokualex_herokusbosio_sfsbosio_sfsarahoh2468sarahoh2468tlowrimoretlowrimoreptemporiniptemporinitcareysmith-sfdctcareysmith-sfdcrobinson.mrobinson.mdkabanov1dkabanov1rdagg-herordagg-herok80bowmank80bowmansheax0rsheax0rdpark.herokudpark.herokudaniel.brightdaniel.brightjdowningjdowningabhinavkoulabhinavkoulviniljainviniljaineblackeblackk.plentyk.plentyapadalaapadalalsorstokkelsorstokkercrossrcrosskdreyerorenkdreyerorendhammer25dhammer25dougmcinnes-sfdcdougmcinnes-sfdcchap-herokuchap-herokumichellejhlimmichellejhlimmbuschherokumbuschherokubeckychenbeckychensmrutisamantasmrutisamantaapoorvasrivapoorvasrivhsophie-sfhsophie-sfp.shreyap.shreyasai_harshitha_neelasai_harshitha_neelanlanattanlanattadreichenbergdreichenbergmarsmarsheroku-front-endheroku-front-endrichatiwaririchatiwariwchrisjohnsonwchrisjohnsonthe7okathe7okasimonebonettisfitasimonebonettisfitalucaszhoulucaszhoupsalimpsalimtaylor.jonestaylor.jonesangelayoungangelayoungktsforcektsforcekerry-bennettkerry-bennettgkomminenigkomminenisripad.kulkarnisripad.kulkarnismukusmukudgallegossfdcdgallegossfdcrishabh.wasonrishabh.wasonsarassassinsarassassinarajaarajav.anandv.anandlmckenzielmckenziejordaneekeyjordaneekeydaigo-herokudaigo-herokumimenmimenjwadsworthsfjwadsworthsfpfuentepfuentebleongbleongpaguilarpaguilarzli-sfzli-sfsruthichsruthichjesse.brown-sfjesse.brown-sftij005tij005tholschuh-salesforcetholschuh-salesforcemohammadfazil.shaikmohammadfazil.shaikflavortown_towerflavortown_towerpaulyflemingpaulyflemingricardoherokuricardoherokudasofieidasofieikumardinesh2kumardinesh2sherry.yaosherry.yaotholmes-sfdctholmes-sfdcpalakjeetkaur12palakjeetkaur12sfjonnpmsfjonnpmvjohnevjohnejinjutha.hancockjinjutha.hancockmgauger_herokumgauger_herokutdhayanandatdhayanandakharlowsfkharlowsfjhili!16jhili!16anovak-herokuanovak-herokuslizcoslizcoyasuhiro-herokuyasuhiro-herokuanna-crossanna-crosscromwellryan-sfcromwellryan-sfhyunwookleehyunwookleetm-sftm-sfnkoziuknkoziukherokucodyherokucodydodom-sfdcdodom-sfdccsinghaus-sfdccsinghaus-sfdcjw-sfdcjw-sfdcjlopezdanjlopezdangsinghsfdcgsinghsfdcnrenkenrenkesalesforcegenesalesforcegenebsonntag-salesforcebsonntag-salesforcechetankd10chetankd10heroku-johnnyheroku-johnnyemilyhuang-herokuemilyhuang-herokumichael.malavemichael.malaveerika.wallaceerika.wallacevalluri056valluri056hk-ankitkumarhk-ankitkumarkaruna123karuna123mjoherson-sfmjoherson-sfskediyal-sfskediyal-sf

Readme

heroku-fetch

A modern JavaScript/TypeScript API client for Heroku APIs, built on the Fetch API and designed to work seamlessly in both Node.js and browser environments.

Features

  • Universal: Works in Node.js and browsers
  • 🔐 Authentication: Support for bearer tokens and dynamic token providers
  • 🛡️ 2FA Support: Automatic handling of two-factor authentication challenges
  • 🎯 Multi-API Support: Pre-configured for Platform API, Data API, and Particleboard
  • 📊 Streaming: Native support for streaming responses and Server-Sent Events
  • 🐛 Debugging: Built-in debugging with the debug package
  • 💪 TypeScript: Full TypeScript support with complete type definitions
  • Modern: Built on ky for a clean, promise-based API

Installation

npm install @heroku/heroku-fetch

Quick Start

Basic Usage

import { HerokuApiClient } from '@heroku/heroku-fetch';

// Create a client for the Platform API
// Automatically uses token from HEROKU_API_KEY env var or ~/.netrc
const client = new HerokuApiClient({
  service: 'platform',
});

// Make a request
const response = await client.get('/apps');
const apps = await response.json();
console.log(apps);

Service Configuration

The client comes with pre-configured settings for different Heroku services:

// Platform API (default)
const platformClient = new HerokuApiClient({
  service: 'platform',
  token: 'your_token',
});

// Data API
const dataClient = new HerokuApiClient({
  service: 'data',
  token: 'your_token',
});

// Data API with EU region
const euDataClient = new HerokuApiClient({
  service: 'data',
  region: 'eu',
  token: 'your_token',
});

// Particleboard
const particleboardClient = new HerokuApiClient({
  service: 'particleboard',
  token: 'your_token',
});

// Custom API
const customClient = new HerokuApiClient({
  service: 'custom',
  baseUrl: 'https://your-custom-api.heroku.com',
  token: 'your_token',
});

Authentication

Automatic Token from Environment or Netrc (Default)

By default, the client automatically fetches tokens from HEROKU_API_KEY environment variable or ~/.netrc file. No manual token management required!

import { HerokuApiClient } from '@heroku/heroku-fetch';

// Token automatically loaded from HEROKU_API_KEY or ~/.netrc
const client = new HerokuApiClient({
  service: 'platform',
});

The token priority is:

  1. HEROKU_API_KEY environment variable
  2. ~/.netrc file (api.heroku.com machine)

If you need to manually retrieve the token for other purposes, you can use getAuthToken():

import { getAuthToken } from '@heroku/heroku-fetch';

const token = getAuthToken(); // Returns string | undefined

Static Bearer Token

const client = new HerokuApiClient({
  service: 'platform',
  token: 'your_static_heroku_bearer_token',
});

Dynamic Token Provider

Use a function to dynamically retrieve or refresh tokens:

import { getAuthTokenProvider } from '@heroku/heroku-fetch';

// Option 1: Use the built-in provider for dynamic token fetching
const client = new HerokuApiClient({
  service: 'platform',
  token: getAuthTokenProvider(), // Returns a function that fetches token each time
});

// Option 2: Provide your own custom token function
const client = new HerokuApiClient({
  service: 'platform',
  token: async () => {
    // Fetch token from secure store or refresh mechanism
    const token = await fetchFreshHerokuToken();
    return token;
  },
});

Two-Factor Authentication

Handle 2FA challenges automatically:

const client = new HerokuApiClient({
  service: 'platform',
  token: 'your_token',
  twoFactor: {
    onChallenge: async () => {
      // Prompt user for 2FA code
      const code = await promptUserFor2FA();
      return code;
    },
  },
});

HTTP Methods

The client provides convenience methods for common HTTP operations:

GET

const response = await client.get('/apps');
const apps = await response.json();

POST

const response = await client.post('/apps', {
  name: 'my-new-app',
  region: 'us',
});
const app = await response.json();

PUT

const response = await client.put('/apps/my-app', {
  maintenance: true,
});

PATCH

const response = await client.patch('/apps/my-app', {
  name: 'renamed-app',
});

DELETE

const response = await client.delete('/apps/my-app');

Request Options

All HTTP methods accept an optional RequestOptions object:

const response = await client.get('/apps', {
  headers: {
    'X-Custom-Header': 'value',
  },
  timeout: 5000, // Override default timeout
  searchParams: {
    limit: 10,
    offset: 0,
  },
});

Cancellation

Pass an AbortSignal to cancel an in-flight request. Aborting rejects the returned promise with an AbortError:

const controller = new AbortController();

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

try {
  const response = await client.get('/apps', {signal: controller.signal});
  const apps = await response.json();
  clearTimeout(timer);
  console.log(apps);
} catch (error) {
  if ((error as Error).name === 'AbortError') {
    console.log('Request cancelled');
  } else {
    throw error;
  }
}

signal is also forwarded to client.stream(), so the same controller can cancel a long-lived streaming response.

Streaming

Stream data from endpoints like Logplex:

const response = await client.stream('/apps/my-app/log-sessions/session-id');

// Node.js - use the response body as a stream
if (response.body) {
  const reader = response.body.getReader();
  const decoder = new TextDecoder();

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    const chunk = decoder.decode(value, { stream: true });
    console.log(chunk);
  }
}

Server-Sent Events (SSE)

const response = await client.stream('/apps/my-app/log-sessions/session-id');

// Process SSE events
const reader = response.body?.getReader();
const decoder = new TextDecoder();

if (reader) {
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    const text = decoder.decode(value);
    // Parse SSE format: "data: {...}\n\n"
    const events = text.split('\n\n').filter(Boolean);

    for (const event of events) {
      if (event.startsWith('data: ')) {
        const data = event.slice(6);
        console.log('Event:', data);
      }
    }
  }
}

Error Handling

The client throws specific error types for different scenarios:

import {
  HerokuApiError,
  AuthenticationError,
  NotFoundError,
  TwoFactorRequiredError,
  RateLimitError,
} from '@heroku/heroku-fetch';

try {
  const response = await client.get('/apps/nonexistent');
} catch (error) {
  if (error instanceof NotFoundError) {
    console.error('App not found');
  } else if (error instanceof AuthenticationError) {
    console.error('Invalid credentials');
  } else if (error instanceof RateLimitError) {
    console.error(`Rate limited. Retry after ${error.retryAfter} seconds`);
  } else if (error instanceof TwoFactorRequiredError) {
    console.error('2FA required but not configured');
  } else if (error instanceof HerokuApiError) {
    console.error(`API Error: ${error.message}`);
    console.error(`Status: ${error.statusCode}`);
    console.error(`ID: ${error.id}`);
    console.error(`Errors: ${JSON.stringify(error.errors)}`);
  }
}

Debugging

Enable debugging output using the DEBUG environment variable (Node.js) or localStorage.debug (browser):

# Enable all heroku-fetch debugging
DEBUG=heroku-fetch:* node your-script.js

# Enable specific namespaces
DEBUG=heroku-fetch:request,heroku-fetch:response node your-script.js

Available debug namespaces:

  • heroku-fetch:request - Outgoing HTTP requests
  • heroku-fetch:response - Incoming HTTP responses
  • heroku-fetch:auth - Authentication and token management
  • heroku-fetch:error - Error details

In the browser:

localStorage.debug = 'heroku-fetch:*';

Configuration Options

interface HerokuApiClientOptions {
  /** Heroku service type */
  service?: 'platform' | 'data' | 'particleboard' | 'custom';

  /** Static bearer token or function to retrieve token */
  token?: string | (() => string | Promise<string>);

  /** Two-factor authentication configuration */
  twoFactor?: {
    onChallenge: () => string | Promise<string>;
  };

  /** Custom base URL (required for 'custom' service) */
  baseUrl?: string;

  /** Service region (e.g., 'eu', 'us') */
  region?: string;

  /** Request timeout in milliseconds (default: 30000) */
  timeout?: number;

  /** Additional custom headers */
  headers?: Record<string, string>;

  /** Enable debug output */
  debug?: boolean;
}

Updating Options

You can update client options after instantiation:

const client = new HerokuApiClient({ service: 'platform' });

// Update token
client.setOption('token', 'new_token');

// Update timeout
client.setOption('timeout', 5000);

TypeScript Support

The library is written in TypeScript and provides full type definitions:

import type {
  HerokuApiClientOptions,
  RequestOptions,
  HerokuService,
  TokenProvider,
} from '@heroku/heroku-fetch';

Examples

Create and Deploy an App

const client = new HerokuApiClient({
  service: 'platform',
  token: process.env.HEROKU_TOKEN,
});

// Create app
const createResponse = await client.post('/apps', {
  name: 'my-awesome-app',
  region: 'us',
});
const app = await createResponse.json();
console.log('Created app:', app.name);

// Get app details
const appResponse = await client.get(`/apps/${app.name}`);
const appDetails = await appResponse.json();
console.log('App details:', appDetails);

Stream Logs

const client = new HerokuApiClient({
  service: 'platform',
  token: process.env.HEROKU_TOKEN,
});

// Create log session
const sessionResponse = await client.post('/apps/my-app/log-sessions', {
  dyno: 'web.1',
  lines: 100,
  tail: true,
});
const session = await sessionResponse.json();

// Stream logs
const logsResponse = await client.stream(session.logplex_url);
const reader = logsResponse.body?.getReader();
const decoder = new TextDecoder();

if (reader) {
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    const logs = decoder.decode(value, { stream: true });
    process.stdout.write(logs);
  }
}

Work with Postgres

const client = new HerokuApiClient({
  service: 'data',
  token: process.env.HEROKU_TOKEN,
});

// List databases
const response = await client.get('/databases');
const databases = await response.json();
console.log('Databases:', databases);

Oclif CLI Commands

Complete examples for building oclif CLI commands are available in examples/oclif/:

import { Command } from '@oclif/core';
import { HerokuApiClient } from '@heroku/heroku-fetch';

export default class AppsList extends Command {
  async run() {
    // Client automatically uses token from env or netrc
    const client = new HerokuApiClient({
      service: 'platform',
    });

    const response = await client.get('/apps');
    const apps = await response.json();

    // Display apps...
  }
}

See examples/oclif/README.md for complete working examples including:

  • Listing apps with table formatting
  • Creating apps with validation
  • Streaming logs in real-time
  • Error handling patterns
  • 2FA support

Contributing

Contributions are welcome! Please see the repository for contribution guidelines.

License

MIT

Related Projects

  • ky - The underlying HTTP client
  • debug - Debugging utility

Support

For issues and questions, please visit the GitHub repository.