Skip to content

JavaScript / TypeScript SDK

Terminal window
pnpm add gunsole-js

Works in browsers and Node.js 18+. Zero runtime dependencies.

import { createGunsoleClient } from "gunsole-js";
const gun = createGunsoleClient({
projectId: "my-app",
apiKey: "dev",
mode: "local",
});
OptionTypeDefaultDescription
projectIdstringrequiredIdentifies your project in the desktop app
apiKeystringAPI key (required for cloud mode, any string for local)
mode"local" | "desktop" | "cloud"requiredDetermines the endpoint URL
endpointstringOverride the endpoint URL entirely
envstringEnvironment label (e.g. "production", "staging")
appNamestringApplication name, attached to every log
appVersionstringApplication version, attached to every log
defaultTagsRecord<string, string>Tags merged into every log entry
batchSizenumber10Number of logs to collect before auto-flushing
flushIntervalnumber5000Auto-flush interval in milliseconds
fetchFetchFunctionglobalThis.fetchCustom fetch implementation
isDebugbooleanfalseDisables gzip compression for debugging
bucketsstring[]Typed bucket accessors with autocomplete
ModeEndpoint
localhttp://localhost:17655
desktophttp://localhost:8787
cloudhttps://api.gunsole.com

Use local during development with the desktop app. The endpoint option overrides the mode-based URL if you need a custom address.

Four methods, one for each level:

gun.info({ bucket: "api", message: "Request handled" });
gun.debug({ bucket: "api", message: "Cache miss", context: { key: "user:123" } });
gun.warn({ bucket: "api", message: "Slow query", context: { duration: 2500 } });
gun.error({ bucket: "api", message: "Connection refused", tags: { db: "postgres" } });

gun.log() accepts an optional level as the first argument:

gun.log({ bucket: "api", message: "Request handled" }); // defaults to "info"
gun.log("error", { bucket: "api", message: "Something broke" }); // explicit level
FieldTypeRequiredDescription
messagestringyesThe log message
bucketstringyesCategory/namespace for this log
contextRecord<string, unknown>noArbitrary structured data
tagsRecord<string, string>noKey-value pairs for filtering (string values only)
traceIdstringnoLinks related logs across operations

Each log entry is internally enriched before sending:

{
// from your call
bucket: "api",
message: "Request handled",
level: "info",
context: { ... },
tags: { ...defaultTags, ...yourTags }, // merged
// auto-populated
timestamp: 1708214400000, // Date.now()
userId: "u_123", // from setUser()
sessionId: "sess_abc", // from setSessionId()
env: "production", // from config
appName: "my-app", // from config
appVersion: "1.0.0", // from config
}
gun.setUser({
id: "u_123",
email: "ada@example.com",
name: "Ada Lovelace",
traits: {
plan: "pro",
team: "backend",
},
});
gun.setSessionId("sess_abc123");

Once set, userId and sessionId are attached to every subsequent log. Useful for filtering logs by user in the desktop app.

Tags are flat key-value pairs (string → string) that become dynamic filters in the desktop app.

gun.info({
bucket: "api",
message: "POST /users",
tags: {
route: "/users",
method: "POST",
status: "201",
},
});

Default tags from config are merged with per-log tags. Per-log tags win on conflict.

const gun = createGunsoleClient({
projectId: "my-app",
apiKey: "dev",
mode: "local",
defaultTags: { service: "api-server", region: "us-east-1" },
});
// Sent tags: { service: "api-server", region: "us-east-1", route: "/users" }
gun.info({ bucket: "api", message: "Request", tags: { route: "/users" } });

For compile-time tag safety with TypeScript, see Typed Tags.

Logs are buffered and sent in batches. A batch is flushed when:

  • The batch reaches batchSize (default: 10)
  • The flushInterval timer fires (default: 5000ms)
  • You call gun.flush() manually
await gun.flush();

The SDK compresses payloads with gzip by default. Set isDebug: true to send plain JSON (useful for inspecting network requests).

The SDK is built to never crash your app. All internal errors are silently caught. Failed HTTP requests are retried up to 3 times with exponential backoff (1s, 2s, 4s).

Automatically catch unhandled errors and promise rejections:

gun.attachGlobalErrorHandlers();

In the browser, this listens to window.addEventListener("error") and window.addEventListener("unhandledrejection").

In Node.js, this listens to process.on("uncaughtException") and process.on("unhandledRejection").

Captured errors are logged as error level to these buckets: global_error, unhandled_rejection, uncaught_exception.

gun.detachGlobalErrorHandlers();

When your app is shutting down (or a component unmounts):

gun.destroy();

This flushes remaining logs, clears timers, and detaches global error handlers.

// React
useEffect(() => {
const gun = createGunsoleClient({ ... });
return () => gun.destroy();
}, []);