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

@cherrypeak-org/cherryboard-client

v1.1.0

Published

CherryBoard error tracking client for Node.js — captures and sends application errors to CherryBoard Dashboard

Readme

@cherrypeak-org/cherryboard-client

A lightweight, zero-dependency Node.js/TypeScript client for capturing and sending application errors to the CherryBoard Dashboard — the JavaScript equivalent of the CherryPeak.CherryBoard.Client NuGet package for .NET.

Features

  • Automatic error capture via Lambda handler wrapper or Express middleware
  • In-memory queue with configurable batch size and interval
  • Exponential-backoff retries (skips 4xx client errors)
  • Sampling — configurable capture rate from 0% to 100%
  • Zero runtime dependencies — uses native fetch (Node 18+)
  • TypeScript-first with full type declarations

Requirements

  • Node.js 18+ (for native fetch)

Installation

npm install @cherrypeak-org/cherryboard-client

Requires Node 18 or later — the client uses the built-in fetch, so it has no runtime dependencies of its own.

The API key

Create one in the CherryBoard dashboard under Project → Environment → API keys, and use a Server key: it can read and manage data, so it belongs in secrets management and never in a commit or a client bundle.

# Development
CHERRYBOARD_API_KEY=cpd_xxxxxxxxxxxxxxxx

The key determines which project and environment your reports land in, so use a separate one per environment — revoking a leaked key then affects only that environment.

Quick Start

1. Create a client instance

import { createCherryBoardClient } from "@cherrypeak-org/cherryboard-client";

const client = createCherryBoardClient({
  apiKey: process.env.CHERRYBOARD_API_KEY!,
  apiUrl: "https://api.cherryboard.cherrypeak.eu",
  environment: process.env.NODE_ENV ?? "production",
});

2a. AWS Lambda (SST / Serverless)

import { createCherryBoardClient, withCherryBoard } from "@cherrypeak-org/cherryboard-client";

const client = createCherryBoardClient({
  apiKey: process.env.CHERRYBOARD_API_KEY!,
  apiUrl: process.env.CHERRYBOARD_API_URL!,
  environment: process.env.SST_STAGE ?? "production",
});

// Wrap your handler — errors are captured automatically and the queue is
// flushed after every invocation (critical for Lambda's freeze behavior).
export const handler = withCherryBoard(async (event, context) => {
  // ... your handler logic ...
  return { statusCode: 200, body: JSON.stringify({ ok: true }) };
}, client);

2b. Express

import express from "express";
import {
  createCherryBoardClient,
  cherryBoardErrorHandler,
} from "@cherrypeak-org/cherryboard-client";

const app = express();
const client = createCherryBoardClient({
  apiKey: process.env.CHERRYBOARD_API_KEY!,
  apiUrl: process.env.CHERRYBOARD_API_URL!,
});

// Your routes...
app.get("/", (req, res) => {
  res.send("Hello");
});

// Register the error handler AFTER all routes
app.use(cherryBoardErrorHandler(client));

app.listen(3000);

3. Manual capture

try {
  await riskyOperation();
} catch (err) {
  await client.captureException(err as Error, (data) => {
    data.userId = currentUser.id;
    data.metadata = JSON.stringify({ orderId: "123" });
  });
}

Or send a pre-built error:

import { createErrorData, Severity } from "@cherrypeak-org/cherryboard-client";

await client.captureError(
  createErrorData({
    message: "Payment failed",
    severity: Severity.Critical,
    userId: user.id,
    metadata: JSON.stringify({ provider: "stripe", code: "card_declined" }),
  })
);

Configuration

| Option | Type | Default | Valid Range | Description | | --- | --- | --- | --- | --- | | apiKey | string | — | Required | API key (X-API-Key header) | | apiUrl | string | — | Required | Base URL of Dashboard API | | environment | string | "Production" | Any | Environment label | | enableAutomaticCapture | boolean | true | — | Enable middleware/wrapper auto-capture | | maxBatchSize | number | 50 | 1–1000 | Queue threshold before immediate flush | | batchIntervalSeconds | number | 30 | 1–3600 | Timer interval for queue drain | | maxRetries | number | 3 | 0–10 | Retry attempts for failed HTTP calls | | enableOfflineQueue | boolean | true | — | Use in-memory queue + batch sending | | sampleRate | number | 1.0 | 0.0–1.0 | Probability of capturing each error | | disableSslValidation | boolean | false | — | Skip SSL cert validation (dev only) |

API Reference

createCherryBoardClient(options, logger?)

Factory function. Returns a CherryBoardClient instance.

CherryBoardClient

| Method | Returns | Description | | --- | --- | --- | | captureException(error, configure?) | Promise<string \| null> | Capture a JS Error with auto-populated fields | | captureError(errorData) | Promise<string \| null> | Send a pre-built ErrorData | | sendBatch(errors) | Promise<BatchErrorResponse \| null> | Send multiple errors in one request | | flush() | Promise<void> | Drain the internal queue immediately | | destroy() | Promise<void> | Stop batch timer + flush (for graceful shutdown) |

withCherryBoard(handler, client, options?)

Wraps an AWS Lambda handler. Auto-captures errors and flushes after every invocation.

cherryBoardErrorHandler(client)

Returns an Express error-handling middleware ((err, req, res, next)).

Severity Levels

import { Severity } from "@cherrypeak-org/cherryboard-client";

Severity.Debug    // 0
Severity.Info     // 1
Severity.Warning  // 2
Severity.Error    // 3
Severity.Critical // 4

Logging

The client accepts an optional logger matching this interface:

interface Logger {
  debug(message: string, ...args: unknown[]): void;
  info(message: string, ...args: unknown[]): void;
  warn(message: string, ...args: unknown[]): void;
  error(message: string, ...args: unknown[]): void;
}

Pass console, pino(), winston.createLogger(), or any compatible logger:

import pino from "pino";

const client = createCherryBoardClient(
  { apiKey: "...", apiUrl: "..." },
  pino()
);

Comparison with .NET Client

| Feature | .NET (NuGet) | Node.js (this package) | | --- | --- | --- | | Queue | ConcurrentQueue<T> | Array (single-threaded) | | Batch timer | System.Threading.Timer | setInterval (unref'd) | | HTTP | HttpClient + DI | Native fetch | | Middleware | ASP.NET Core middleware | Lambda wrapper / Express middleware | | DI | IServiceCollection.AddCherryBoard() | createCherryBoardClient() factory | | Retry | Exponential backoff, skip 4xx | Same | | Lambda support | N/A | withCherryBoard() with auto-flush |

Request timing

Times each request and reports per-route rollups to the dashboard, where they appear alongside the browser SDK's numbers for the same route.

Off by default. Two things to enable it:

import express from "express";
import {
    createCherryBoardClient,
    cherryBoardPerformance,
    cherryBoardErrorHandler,
} from "@cherrypeak-org/cherryboard-client";

const client = createCherryBoardClient({
    apiKey: process.env.CHERRYBOARD_API_KEY!,
    apiUrl: "https://api.cherryboard.cherrypeak.eu",
    enablePerformanceTracking: true,
});

const app = express();

// Register early. Anything above this line is time the measurement never sees.
app.use(cherryBoardPerformance(client.metrics!));

app.get("/api/orders/:id", handler);

// Error handling stays last.
app.use(cherryBoardErrorHandler(client));

Call client.destroy() on shutdown so the final window is sent rather than lost with the process.

Why measure both sides

The browser SDK already reports how long API calls take. That figure includes DNS, TLS, the network and transferring the response; this one is server processing alone.

| What you see | What it points at | |---|---| | Both numbers similar | Your code — the time is in the handler | | Frontend much higher | Payload size or the network, not the handler | | Backend high, few calls | A slow dependency on a rarely-hit endpoint |

What it costs

A timestamp per request and an in-memory counter update. Rollups are posted on a timer, so the payload is bounded by how many endpoints the service has rather than how much traffic it serves — a service handling a million requests sends the same small summary as one handling a hundred.

Delivery failures are swallowed by design. A dropped window is a gap in a chart; a thrown exception would be an outage.

What is not recorded

Responses the server holds open are skipped automatically: server-sent events (text/event-stream) and protocol upgrades (HTTP 101). Their elapsed time is how long the client stayed subscribed, not how long anything took to compute — left in, a healthy stream sits permanently at the top of the slowest-routes table.

For anything else slow by design — long-polling, large downloads — mark the handler:

import { cherryBoardIgnorePerformance } from "@cherrypeak-org/cherryboard-client";

app.get("/download/:id", cherryBoardIgnorePerformance(async (req, res) => {
    // ...
}));

Use it sparingly: a route that is genuinely slow is what this feature exists to show you.

What gets recorded

Paths are normalized before anything is stored, so /api/orders/12345 becomes /api/orders/:id. This uses the same rules as the browser SDK and the .NET client deliberately — the dashboard pairs a route measured in the browser with the same route measured on the server, and that pairing is a string match, so the implementations have to agree.

  • Query strings are never recorded — they carry tokens and email addresses, and say nothing about how long a request took.
  • 404s group under one label, since the set of paths nobody serves is unbounded.
  • 4xx is not counted as an error: a caller's bad request is not your endpoint failing.

License

MIT