Skip to main content

Node.js SDK

The @bataudit/node SDK provides automatic HTTP audit logging for Express and Fastify applications.

Installation​

npm install @bataudit/node
# or
pnpm add @bataudit/node

Express​

import express from 'express'
import { createExpressMiddleware } from '@bataudit/node'

const app = express()
app.use(express.json())

app.use(createExpressMiddleware({
apiKey: 'bat_your_api_key',
serviceName: 'my-api',
writerUrl: 'http://localhost:8081',
environment: 'production',
}))

app.listen(3000)

Every request is automatically logged after the response is sent.

Attaching user context​

The middleware reads user data from req.bataudit, which you set in your auth middleware:

app.use(async (req, res, next) => {
const user = await verifyToken(req.headers.authorization)
if (user) {
req.bataudit = {
identifier: user.id,
userEmail: user.email,
userName: user.name,
userRoles: user.roles,
userType: user.type,
tenantId: user.organizationId,
}
}
next()
})

If req.bataudit is not set, identifier defaults to 'anonymous'.

Capturing request & response bodies​

Bodies are not captured by default to avoid logging sensitive data accidentally. Each direction is opt-in independently:

app.use(createExpressMiddleware({
apiKey: 'bat_your_api_key',
serviceName: 'my-api',
writerUrl: 'http://localhost:8081',
captureBody: true, // ← request body
captureResponseBody: true, // ← response body
}))
  • captureBody — captures the incoming request body.
  • captureResponseBody — captures the JSON response sent back. On Express the SDK wraps res.json; on Fastify it uses the onSend hook.

In the dashboard, the response body is hidden by default in the event detail — click Show to reveal it, since it may contain sensitive data even after masking.

warning

BatAudit masks sensitive keys server-side — password, secret, token, api_key, access_token, refresh_token, authorization, and credit card patterns are replaced with ********. Still, review what your API accepts and returns before enabling body capture in production.


Fastify​

import Fastify from 'fastify'
import { applyBatAuditPlugin } from '@bataudit/node'

const app = Fastify()

applyBatAuditPlugin(app, {
apiKey: 'bat_your_api_key',
serviceName: 'my-api',
writerUrl: 'http://localhost:8081',
environment: 'production',
})

await app.listen({ port: 3000 })

Attaching user context (Fastify)​

app.addHook('onRequest', async (request) => {
const user = await verifyToken(request.headers.authorization)
if (user) {
request.bataudit = {
identifier: user.id,
userEmail: user.email,
}
}
})

Configuration​

OptionTypeRequiredDefaultDescription
apiKeystring✅—API key from dashboard
serviceNamestring✅—Name of this service
writerUrlstring✅—BatAudit Writer URL
environmentstring—prodprod, staging, dev
captureBodyboolean—falseCapture request bodies
captureResponseBodyboolean—falseCapture JSON response bodies

AWS Lambda​

BatAudit sends events asynchronously in the background. In AWS Lambda, the process may be frozen before the HTTP request to the Writer completes, causing events to be lost.

Workaround: Await the log call explicitly at the end of the handler:

import { BatAuditClient } from '@bataudit/node'

const client = new BatAuditClient({
apiKey: 'bat_your_api_key',
serviceName: 'my-lambda',
writerUrl: 'https://your-writer-url',
})

export const handler = async (event) => {
const result = await processEvent(event)

// Await explicitly before Lambda freezes
await client.send({
path: event.path,
method: event.httpMethod,
status_code: result.statusCode,
identifier: event.requestContext?.identity?.user ?? 'anonymous',
service_name: 'my-lambda',
environment: 'production',
timestamp: new Date().toISOString(),
})

return result
}

TypeScript types​

import type { BatAuditConfig, BatAuditRequestData, AuditEvent } from '@bataudit/node'