> ## Documentation Index
> Fetch the complete documentation index at: https://docs.peeng.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Peeng JavaScript SDK — Node.js, Browser & Edge Logging

> Official Peeng JS/TS SDK for Node 18+, browsers, and edge runtimes — non-blocking log calls with automatic batching, retries, and idempotency.

The official Peeng JavaScript and TypeScript SDK (`npm install peeng`) gives you a fully-typed client that wraps `POST /v1/logs` with non-blocking queuing, automatic batching, retries, and idempotency. Every `peeng.info(...)` call enqueues the log and returns immediately — the SDK handles delivery entirely in the background, so logging never blocks your app or throws exceptions into it.

<Info>
  MIT licensed. Requires Node ≥ 18 (uses the built-in global `fetch` and
  `crypto.randomUUID`); also works in browsers and edge runtimes.
</Info>

## Install

<CodeGroup>
  ```bash npm theme={null}
  npm install peeng
  ```

  ```bash yarn theme={null}
  yarn add peeng
  ```

  ```bash pnpm theme={null}
  pnpm add peeng
  ```
</CodeGroup>

## Quick start

Use ESM imports in TypeScript or modern Node.js:

```ts theme={null}
import { PeengClient } from 'peeng';

const peeng = new PeengClient({
  apiKey: process.env.PEENG_API_KEY!,
  environment: 'production',
  service: 'checkout-api',
});

peeng.info('Order placed', { statusCode: 200, requestId: 'req_123', userId: 'user_456' });

peeng.error('Payment failed', {
  statusCode: 500,
  metadata: { orderId: 'order_789' },
  stackTrace: err.stack,
});
```

CommonJS works the same way — swap `import` for `require`:

```js theme={null}
const { PeengClient, LogLevel } = require('peeng');

const peeng = new PeengClient({ apiKey: process.env.PEENG_API_KEY });

peeng.warn('Slow query', { statusCode: 200, metadata: { durationMs: 1200 } });
```

## Why use the SDK

<CardGroup cols={2}>
  <Card title="Background batching" icon="layer-group">
    `peeng.info(...)` returns immediately — it enqueues, it never blocks on
    network I/O. The buffer flushes automatically every `flushIntervalMs`,
    or as soon as it hits `maxBatchSize`.
  </Card>

  <Card title="Automatic retries" icon="rotate">
    Transient failures (network errors, `429`s, `5xx`s) are retried with
    backoff, reusing one `Idempotency-Key` per batch so retries can't
    double-insert.
  </Card>

  <Card title="Never throws" icon="shield">
    Delivery failures go to `onError` instead of raising out of a logging
    call — a Peeng outage should never crash your app.
  </Card>

  <Card title="Fully typed" icon="code">
    Ships CJS, ESM, and `.d.ts` declarations — full autocomplete on log
    options in every editor.
  </Card>
</CardGroup>

## Constructor options

Pass an options object to `new PeengClient(options)` to configure the client.

| Option            | Type                       | Default                 | Description                                                                      |
| ----------------- | -------------------------- | ----------------------- | -------------------------------------------------------------------------------- |
| `apiKey`          | `string`                   | required                | Your Peeng project API key.                                                      |
| `baseUrl`         | `string`                   | `http://localhost:3000` | Peeng API base URL — set to `https://api.peeng.dev` for real traffic.            |
| `environment`     | `string`                   | —                       | Default `environment` for logs that don't set their own.                         |
| `service`         | `string`                   | —                       | Default `service` for logs that don't set their own.                             |
| `maxBatchSize`    | `number`                   | `100`                   | Flush once the buffer reaches this size. Hard-capped at `500`.                   |
| `flushIntervalMs` | `number`                   | `2000`                  | How often the background timer flushes the buffer (in milliseconds).             |
| `maxRetries`      | `number`                   | `3`                     | Retry attempts on top of the initial try for network errors, `429`s, and `5xx`s. |
| `onError`         | `(error: unknown) => void` | —                       | Called with errors from failed background flushes.                               |

## Logging methods

The SDK exposes one convenience method per log level:

```ts theme={null}
peeng.debug(message: string, opts: LogOptions): void
peeng.info(message: string, opts: LogOptions): void
peeng.warn(message: string, opts: LogOptions): void
peeng.error(message: string, opts: LogOptions): void
peeng.fatal(message: string, opts: LogOptions): void
```

`opts.statusCode` is required on every call (100–599). The following fields are optional: `service`, `metadata`, `stackTrace`, `hostname`, `requestId`, `userId`, `timestamp`, and a per-call `environment` override. See the [field reference](/api-reference/logs/ingest) for what each one means.

For anything the convenience methods don't cover, use the generic form:

```ts theme={null}
peeng.log({
  level: LogLevel.DEBUG,
  message: 'Cache miss',
  statusCode: 200,
  environment: 'staging',
});
```

## Graceful shutdown

Call `flush()` to send whatever is currently in the buffer right now, and `close()` to stop the background timer and flush any remaining logs before your process exits.

```ts theme={null}
await peeng.flush(); // send whatever's currently buffered right now
await peeng.close(); // stop the background timer and flush remaining logs
```

Hook into your process's `SIGTERM` signal to give the client time to drain before shutdown:

```ts theme={null}
process.on('SIGTERM', async () => {
  await peeng.close();
  process.exit(0);
});
```

<Note>
  The client also best-effort auto-flushes on Node's `beforeExit` event as a
  safety net — `close()` is the deterministic way to guarantee delivery.
</Note>

## Custom batching config

Override the default batching and retry settings at construction time:

```ts theme={null}
const peeng = new PeengClient({
  apiKey: process.env.PEENG_API_KEY!,
  environment: 'production',
  maxBatchSize: 250,
  flushIntervalMs: 5000,
  maxRetries: 5,
  onError: (err) => {
    console.error('peeng flush failed', err);
  },
});
```

## Error handling

Background delivery failures are routed to `onError`, but you can also `await peeng.flush()` directly and catch errors in a `try/catch` block. The SDK surfaces two error types:

* **`PeengApiError`** — the server responded with an error envelope. Carries `statusCode`, `message`, `error` (from the server envelope), and a `context` object with `batchSize`, `idempotencyKey`, and `url`. This is thrown for non-retryable errors (e.g. `400`) or after retries are exhausted (e.g. repeated `5xx`).
* **Plain `Error`** — a network failure with no HTTP response at all (DNS failure, connection refused), surfaced after retries are exhausted.

```ts theme={null}
import { PeengApiError } from 'peeng';

try {
  await peeng.flush();
} catch (err) {
  if (err instanceof PeengApiError) {
    console.error(`Peeng API error ${err.statusCode}: ${err.message}`);
  } else {
    console.error('Peeng flush failed', err);
  }
}
```

See [Errors](/essentials/errors) for the full retryable/non-retryable rules.
