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 secretTwo pieces:
- Browser SDK (this package) — 7 KB gzipped, zero dependencies,
fetch-only. - Relay — a tiny Deno/Node function that imports
@logdbhq/node. Template included attemplates/supabase-edge-function/.
Install
npm install @logdbhq/webUsage
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