JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 32
  • Score
    100M100P100Q81365F
  • License MIT

LogDB SDK for browsers. Paste a publishable key, log from the browser — the hosted relay handles CORS, origin validation, and forwarding to LogDB. Self-hosted relays (Supabase Edge / Cloudflare Worker / Next.js) still supported.

Package Exports

  • @logdbhq/web
  • @logdbhq/web/builders
  • @logdbhq/web/models

Readme

@logdbhq/web

Browser logging SDK for LogDB. Posts JSON batches to a relay you deploy; the relay forwards to LogDB via @logdbhq/node over gRPC-Web. Your LogDB API key never ships in the browser bundle.

Status: v0.1.0-alpha. Writer-only.

Architecture

Browser ──JSON batch──► Your Relay (Supabase Edge / Worker) ──gRPC-Web──► LogDB
                         uses @logdbhq/node
                         holds LOGDB_API_KEY secret

Two pieces:

  1. Browser SDK (this package) — 7 KB gzipped, zero dependencies, fetch-only.
  2. Relay — a tiny Deno/Node function that imports @logdbhq/node. Template included at templates/supabase-edge-function/.

Install

npm install @logdbhq/web

Usage

import { LogDBClient, LogLevel } from "@logdbhq/web";

export const logdb = new LogDBClient({
  endpoint: "https://<project-ref>.supabase.co/functions/v1/logdb-relay",
  defaultApplication: "my-react-app",
  defaultEnvironment: import.meta.env.MODE,
});

await logdb.log({ message: "checkout clicked", level: LogLevel.Info });

No apiKey. No authorization. No apikey header. The relay takes care of all of it.

Relay setup (Supabase Edge Function)

Full walkthrough in templates/supabase-edge-function/README.md. Short version:

mkdir -p supabase/functions/logdb-relay
cp node_modules/@logdbhq/web/templates/supabase-edge-function/index.ts \
   supabase/functions/logdb-relay/index.ts

supabase secrets set LOGDB_API_KEY=your-logdb-key --project-ref <ref>
supabase functions deploy logdb-relay --no-verify-jwt --project-ref <ref>

The --no-verify-jwt flag matters — without it, Supabase's gateway blocks anonymous browser requests with a 401 before they reach the function.

Reading / querying

The same relay that handles writes also handles reads. Add a LogDBReader pointed at the same endpoint:

import { LogDBReader } from "@logdbhq/web";

const reader = new LogDBReader({
  endpoint: "https://<ref>.supabase.co/functions/v1/logdb-relay",
});

const { items, totalCount } = await reader.getLogs({
  application: "my-react-app",
  level: "Error",
  fromDate: new Date(Date.now() - 24 * 60 * 60 * 1000),
  take: 50,
  sort: { field: "timestamp", ascending: false },
});

const count       = await reader.getLogsCount({ application: "my-react-app" });
const collections = await reader.getCollections();

Methods: getLogs, getLogCaches, getLogBeats, getLogsCount, getCollections, getEventLogStatus. Returns typed { items, totalCount } — works out of the box with TanStack Query useInfiniteQuery (derive hasMore from skip + items.length < totalCount).

The relay template was updated in 0.1.1-alpha.0 to dispatch reads — redeploy it when upgrading:

cp node_modules/@logdbhq/web/templates/supabase-edge-function/index.ts \
   supabase/functions/logdb-relay/index.ts
supabase functions deploy logdb-relay --no-verify-jwt --project-ref <ref>

Fluent builder

import { LogEventBuilder, LogLevel } from "@logdbhq/web";

await LogEventBuilder.create(logdb)
  .setMessage("payment processed")
  .setLogLevel(LogLevel.Info)
  .setUserEmail("alice@example.com")
  .setCorrelationId(traceId)
  .addAttribute("amount_eur", 199.99)
  .addAttribute("currency", "EUR")
  .addLabel("payment")
  .log();

Heartbeats and cache

import { LogBeatBuilder, LogCacheBuilder } from "@logdbhq/web";

await LogBeatBuilder.create(logdb)
  .setMeasurement("active_users")
  .addTag("region", "eu-west-1")
  .addField("count", 1247)
  .log();

await LogCacheBuilder.create(logdb)
  .setKey("user:42:profile")
  .setValue({ name: "Alice", role: "admin" })
  .log();

Global error capture

window.addEventListener("error", (e) => {
  void logdb.log({
    message: e.message,
    level: LogLevel.Error,
    exception: e.error?.name,
    stackTrace: e.error?.stack,
    requestPath: window.location.pathname,
  });
});

window.addEventListener("unhandledrejection", (e) => {
  void logdb.log({
    message: String(e.reason),
    level: LogLevel.Error,
  });
});

Wire format (browser → relay)

Every batch is a single POST:

POST <endpoint>
Content-Type: application/json

{
  "type": "log" | "logBeat" | "logCache",
  "items": [ { ... }, ... ]
}

Response: 204 No Content on success, 4xx/5xx on failure. The relay turns each request into a sendLogBatch / sendLogBeatBatch / sendLogCacheBatch call against @logdbhq/node.

Configuration

Option Default Description
endpoint — required Your relay URL. The SDK POSTs batches to this exact URL (no path is appended).
defaultApplication undefined Stamped as application on every log. Recommended.
defaultEnvironment "production" Stamped as environment.
defaultCollection "logs" Stamped as collection.
enableBatching true Buffer entries and flush together.
batchSize 100 Max entries per batch before flush.
flushInterval 5000 ms Max time an entry waits before flush.
maxRetries 3 Retry attempts per batch on transient failures.
retryDelay 1000 ms Initial retry delay (exponential backoff).
retryBackoffMultiplier 2.0 Backoff multiplier.
enableCircuitBreaker true Trip circuit on repeated failures.
circuitBreakerFailureThreshold 0.5 Failure rate (0..1) that trips the breaker.
circuitBreakerSamplingDuration 10000 ms Rolling window.
circuitBreakerDurationOfBreak 30000 ms Open-state duration.
requestTimeout 30000 ms Per-request deadline.
headers {} Extra HTTP headers on every request.
onError undefined (err, batch?) => void callback for failed sends.
fetchImpl globalThis.fetch Override fetch (tests).

Error handling

import {
  LogDBClient,
  LogLevel,
  LogResponseStatus,
  LogDBAuthError,
  LogDBNetworkError,
} from "@logdbhq/web";

// Status return value — single sends never throw on transient failures.
const status = await logdb.log({ message: "x", level: LogLevel.Info });
if (status === LogResponseStatus.NotAuthorized) {
  // Relay returned 401/403 (usually means LOGDB_API_KEY is wrong or missing).
}

// Or subscribe to the error event.
logdb.on("error", (err) => {
  if (err instanceof LogDBAuthError) { /* ... */ }
  if (err instanceof LogDBNetworkError) { /* transient; already retried */ }
});

When to use @logdbhq/web vs @logdbhq/node

@logdbhq/web @logdbhq/node
Where it runs Browser only Node, Deno, Bun, Cloudflare Workers, Supabase Edge, browser behind fetch
Transport JSON to relay gRPC-Web directly to LogDB
Holds API key No (relay does) Yes
Bundle ~7 KB gzip n/a (server-only packaging)
Use for React / Vite / Lovable frontends Server-side code + relays

Both packages share the same LogDBClient shape, builders, and types, so switching between them is mostly an import change.

AI prompt (paste into Lovable / Cursor / Claude Code)

A ready-to-paste prompt for AI code tools is at templates/ai-prompt.md.

Documentation

License

MIT