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

@capydb/sdk

v1.23.2

Published

Official TypeScript SDK for the CapyDB control plane API - projects, preview databases, backups, imports, webhooks, and integrations.

Readme

@capydb/sdk

Official TypeScript SDK for the CapyDB control plane - projects, preview databases, backups & PITR restores, imports, Studio SQL, webhooks, and deployment integrations. Every CapyDB project runs in its own isolated database cell; the SDK drives the lifecycle around it.

Generated from the control plane's OpenAPI document (GET https://capydb.dev/api/capydb/v1/openapi.json) with @hey-api/openapi-ts, so every operation and model is fully typed and always in lockstep with the API.

Install

pnpm add @capydb/sdk

Usage

import { createCapyDB } from "@capydb/sdk";

const capydb = createCapyDB({ apiKey: process.env.CAPYDB_API_KEY! });

// List projects
const projects = await capydb.listProjects();

// Create a preview database for a branch and wait on its job
const preview = await capydb.createPreviewDatabase({
  path: { projectID: "prj_..." },
  body: { name: "pr-42", mode: "clone", ttl_hours: 72 },
});

// Fetch connection strings
const connections = await capydb.getPreviewDatabaseConnections({
  path: { previewID: preview.data!.preview.id },
});

Use a project-scoped API key (Dashboard → Settings → API keys) for CI and integrations: it can only touch the one project it was minted for.

Ephemeral databases (no account)

createCapyDB() works without an API key for the three calls that need none: creating a throwaway database, reading it back with its claim token, and destroying it early with the same token. It is destroyed after 72 hours unless claimed.

const anonymous = createCapyDB({});

const { data: created } = await anonymous.createEphemeralDatabase();
// created.claim_token is shown once and cannot be recovered - keep it.

const { data: details } = await anonymous.getEphemeralDatabase({
  path: { projectID: created!.ephemeral_database.project_id },
  headers: { "X-CapyDB-Claim-Token": created!.claim_token },
});
// details.connections.pooled_url once details.ephemeral_database.state === "ready"

// Done with it? Destroy it now and free its slot instead of waiting 72 hours.
await anonymous.destroyEphemeralDatabase({
  path: { projectID: created!.ephemeral_database.project_id },
  headers: { "X-CapyDB-Claim-Token": created!.claim_token },
});

// Or keep it instead: claim it into your organization (this call does need a key).
await capydb.claimEphemeralDatabase({
  path: { projectID: created!.ephemeral_database.project_id },
  body: { claim_token: created!.claim_token },
});

Error handling

Operations do not throw on HTTP errors by default. Every call resolves to a result object with a data/error union plus the underlying request and response:

const { data, error, response } = await capydb.getProject({
  path: { projectID: "prj_..." },
});

if (error !== undefined) {
  // Non-2xx responses: `error` is the parsed JSON error body - the control
  // plane always answers `{ error: "human-readable message" }`. If the body
  // is not JSON, `error` is the raw response text. Network/fetch failures
  // surface here too (e.g. a TypeError), in which case `response` is undefined.
  console.error(`request failed (${response?.status}):`, error);
  return;
}

// On 2xx, `data` is the fully typed response body and `error` is undefined.
console.log(data.project.state);

Prefer exceptions? Pass throwOnError: true on each call. (Setting it through the module-level client.setConfig() does not reach createCapyDB() instances: each one has its own client.) The rejection value is the same parsed error body, and data is then non-optional:

const { data } = await capydb.getProject({
  path: { projectID: "prj_..." },
  throwOnError: true, // rejects with { error: "project not found" } on 404
});

One sharp edge: data is typed from the OpenAPI document, but it is the raw parsed JSON - defensive callers should tolerate null where the server could emit a JSON null for an empty list.

Extensions and alerts

List the Postgres extensions available to a project and enable one - enablement runs as an asynchronous job you can poll:

const { data } = await capydb.listProjectExtensions({
  path: { projectID: "prj_..." },
});
const postgis = data?.extensions.find((e) => e.name === "postgis");

const enable = await capydb.enableProjectExtension({
  path: { projectID: "prj_..." },
  body: { name: "postgis" },
});
const job = await capydb.getJob({ path: { jobID: enable.data!.job.id } });

// Disable later (plain DROP EXTENSION, fails if other objects depend on it)
await capydb.disableProjectExtension({
  path: { projectID: "prj_...", name: "postgis" },
});

Threshold alerts (storage / connection usage against plan limits) can be listed and acknowledged; alert.triggered / alert.resolved webhook events fire on the same lifecycle:

const alerts = await capydb.listProjectAlerts({ path: { projectID: "prj_..." } });
for (const alert of alerts.data?.alerts ?? []) {
  if (alert.resolved_at === undefined && alert.acknowledged_at === undefined) {
    await capydb.acknowledgeProjectAlert({
      path: { projectID: alert.project_id, alertID: alert.id },
    });
  }
}

Development

pnpm install
pnpm generate   # regenerate src/generated from the backend openapi.json
pnpm typecheck
pnpm build      # tsdown → dist/
pnpm test       # vitest: mock-fetch unit tests + live contract tests

pnpm test always runs the mock-fetch unit suite. The contract suite runs against a disposable control plane (dry-run executor) that the global setup boots automatically - via docker compose in ../backend when Docker is available, otherwise natively with the local Go toolchain and a Postgres on 127.0.0.1:5432 - and tears down afterwards. Set CAPYDB_TEST_BASE_URL (and optionally CAPYDB_TEST_ADMIN_TOKEN) to point at an already-running stack; when no stack can be booted the contract suite is skipped.

The generated sources live in src/generated and are committed so consumers can audit exactly what ships.