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

# Node.js & TypeScript SDK — AuditRails Audit Client

> Integrate AuditRails into any Node.js or TypeScript application. Zero dependencies, native fetch, automatic batching, and full TypeScript types included.

The AuditRails Node.js SDK is a lightweight, zero-dependency library that works in any Node.js 18+ application. It ships with full TypeScript type definitions, uses the native `fetch` API (no polyfills needed), and handles all the reliability concerns — batching, retries, and graceful shutdown — so you can focus on recording the events that matter.

## Installation

```bash theme={null}
npm install @auditrails/node
```

The SDK requires **Node.js 18 or later**. No additional dependencies are installed.

## Initialization

Import `AuditRails` and create a single client instance. The only required option is your API key — everything else has a sensible default.

```typescript theme={null}
import { AuditRails } from '@auditrails/node';

const audit = new AuditRails({
  apiKey: process.env.AUDITRAILS_API_KEY,
});
```

Create one client per application and reuse it throughout the process lifetime. Creating multiple clients wastes resources and can result in duplicate shutdown handlers.

### Configuration options

| Option          | Type      | Default                     | Description                                                               |
| --------------- | --------- | --------------------------- | ------------------------------------------------------------------------- |
| `apiKey`        | `string`  | *(required)*                | Your AuditRails API key.                                                  |
| `baseUrl`       | `string`  | `https://api.auditrails.io` | Override the API endpoint.                                                |
| `batchSize`     | `number`  | `100`                       | Maximum events per HTTP request.                                          |
| `flushInterval` | `number`  | `1000` (ms)                 | How often the buffer is flushed, in milliseconds.                         |
| `maxRetries`    | `number`  | `3`                         | Retry attempts on `5xx` responses.                                        |
| `timeout`       | `number`  | `10000` (ms)                | Per-request timeout in milliseconds.                                      |
| `autoShutdown`  | `boolean` | `true`                      | Register `beforeExit`, `SIGTERM`, and `SIGINT` handlers to flush on exit. |
| `logger`        | `Logger`  | silent                      | A logger instance for internal diagnostic messages (errors, retries).     |

The internal flush timer calls `.unref()` on itself, which means it will not prevent your Node.js process from exiting naturally when it is otherwise idle.

## Logging events

### Buffered logging (recommended)

`audit.log()` adds an event to an in-memory buffer and returns immediately. The buffer is flushed to the API in the background every second (or when it reaches `batchSize` events). This method **never throws** — any network or API errors are handled internally.

```typescript theme={null}
audit.log({
  action: 'user.login',
  actorId: 'user_123',
  resource: 'session/sess_abc',
  metadata: {
    ip: '203.0.113.1',
    method: 'oauth2',
  },
});
```

### Direct (immediate) logging

`audit.logDirect()` bypasses the buffer and sends the event immediately. It returns a promise that resolves with the API response — including the `logId` assigned to the event — and **throws on any error**. Use this when you need a confirmed receipt before continuing.

```typescript theme={null}
const response = await audit.logDirect({
  action: 'document.deleted',
  actorId: 'user_456',
  resource: 'document/doc-789',
});

console.log(response.logId); // "01HXYZ..."
```

### Direct batch logging

`audit.logBatchDirect()` sends multiple events in a single HTTP request, bypassing the buffer. Like `logDirect`, it **throws on error**.

```typescript theme={null}
const results = await audit.logBatchDirect([
  { action: 'document.created', actorId: 'user_123', resource: 'document/doc-001' },
  { action: 'document.shared',  actorId: 'user_123', resource: 'document/doc-001' },
]);
```

### Manual flush

Call `audit.flush()` to immediately drain the buffer without shutting down the client.

```typescript theme={null}
await audit.flush();
```

## Framework integration

### Express.js

You can wrap `audit.log()` in Express middleware to capture every request automatically. Logging inside the `res.on('finish', ...)` callback ensures you capture the final HTTP status code.

```typescript theme={null}
import express from 'express';
import { AuditRails } from '@auditrails/node';

const audit = new AuditRails({ apiKey: process.env.AUDITRAILS_API_KEY });

function auditMiddleware(action: string) {
  return (req: express.Request, res: express.Response, next: express.NextFunction) => {
    res.on('finish', () => {
      audit.log({
        action,
        actorId: req.user?.id,
        resource: `${req.method} ${req.path}`,
        metadata: {
          ip: req.ip,
          statusCode: res.statusCode,
        },
      });
    });
    next();
  };
}

// Apply to a specific route
app.delete('/documents/:id', auditMiddleware('document.deleted'), deleteDocumentHandler);
```

### Next.js (App Router)

In serverless environments like Next.js, you should disable `autoShutdown` (the process is long-lived and managed by the framework) and export a singleton from a shared module.

```typescript theme={null}
// lib/audit.ts
import { AuditRails } from '@auditrails/node';

export const audit = new AuditRails({
  apiKey: process.env.AUDITRAILS_API_KEY,
  autoShutdown: false, // Next.js manages the process lifecycle
});
```

```typescript theme={null}
// app/api/documents/route.ts
import { audit } from '@/lib/audit';
import { getServerSession } from 'next-auth';

export async function POST(request: Request) {
  const session = await getServerSession();
  const doc = await createDocument(await request.json());

  audit.log({
    action: 'document.created',
    actorId: session.userId,
    resource: `document/${doc.id}`,
  });

  return Response.json(doc);
}
```

## Error handling

`log()` never throws. For `logDirect()` and `logBatchDirect()`, catch `AuditRailsError` to inspect the failure. `AuditRailsTimeoutError` is a subclass specifically for request timeouts.

```typescript theme={null}
import { AuditRails, AuditRailsError, AuditRailsTimeoutError } from '@auditrails/node';

try {
  await audit.logDirect({ action: 'user.login', actorId: 'user_123' });
} catch (err) {
  if (err instanceof AuditRailsTimeoutError) {
    console.error('Request timed out');
  } else if (err instanceof AuditRailsError) {
    console.error(
      err.code,       // Machine-readable error code, e.g. "INVALID_API_KEY"
      err.statusCode, // HTTP status code, e.g. 401
      err.requestId,  // AuditRails request ID for support
      err.docUrl,     // Link to relevant documentation
      err.retryable,  // Whether retrying the request makes sense
    );
  }
}
```

## Graceful shutdown

When `autoShutdown` is `true` (the default), the SDK automatically registers process-exit handlers (`beforeExit`, `SIGTERM`, `SIGINT`) and flushes any buffered events before the process terminates. You do not need to add anything extra in most applications.

If you need manual control — for example in a worker thread, a test suite, or a framework that manages its own lifecycle — call `audit.close()` explicitly:

```typescript theme={null}
// Flush all pending events and shut down background workers
await audit.close();
```

<Note>
  In serverless and edge environments that reuse process instances (Next.js, Vercel, AWS Lambda with warm starts), set `autoShutdown: false` and call `audit.flush()` at the end of each handler invocation to avoid losing buffered events between requests.
</Note>
