Skip to content

Query API

Use AuditService.query() for newest-first application views and investigations. For a forward, checkpointed export, use scan() instead.

Query a page

typescript
const filters = {
  tenantId: 'tenant-1',
  actorId: 'user-123',
  actorType: 'user',
  action: 'invoice.*',
  targetType: 'Invoice',
  source: 'auto' as const,
  result: 'success' as const,
  from: new Date('2026-08-01T00:00:00.000Z'),
  to: new Date('2026-09-01T00:00:00.000Z'),
  limit: 50,
  includeTotal: false,
};

let page = await auditService.query(filters);

while (page.hasMore) {
  page = await auditService.query({
    ...filters,
    cursor: page.nextCursor!,
  });
}

Rows are ordered newest-first by (created_at, id). The cursor is opaque and records only that ordering boundary; it does not contain the filters. Reuse the same filter set for every page.

includeTotal defaults to true. Set it to false for feeds that do not need an exact count; this skips the separate COUNT(*) query and omits total from the result.

Query options

OptionTypeDescription
tenantIdstringExplicitly scope the read to one tenant
allTenantsbooleanDeliberately omit tenant filtering for an authorized admin read
actorIdstringFilter by actor ID
actorTypestringFilter by actor type
actionstringExact action or * wildcard pattern, such as invoice.*
targetTypestringFilter by target type
targetIdstringFilter by target ID
source'auto' | 'manual'Filter by audit source
result'success' | 'failure'Filter by outcome
fromDateInclusive lower created_at bound
toDateInclusive upper created_at bound
limitnumberPage size; defaults to 50 and must be a positive integer
cursorstringContinue below the previous page's nextCursor
offsetnumberNon-negative offset for non-cursor pagination
includeTotalbooleanInclude total; defaults to true

cursor and offset are mutually exclusive. Prefer cursors for a changing or large table. Literal SQL wildcard characters in an action filter are escaped; only * has wildcard meaning.

Response

typescript
interface AuditQueryResult {
  entries: AuditEntry[];
  nextCursor: string | null;
  hasMore: boolean;
  total?: number;
}

nextCursor is non-null only when another page exists. Treat it as an opaque token and do not parse, edit, or manufacture one.

Tenant boundary

query() can use ambient tenant context. An explicit tenantId overrides that ambient context; allTenants: true is the intentional cross-tenant path. tenantId and allTenants are mutually exclusive.

With tenantRequired: true, a call without explicit or ambient tenant scope fails. Without it, an unscoped call is allowed and emits a one-time warning. The package does not authorize admin access, so check cross-tenant permissions before calling allTenants: true.

Look up one entry

getById() follows the same tenant rules and returns null for an invalid or missing ID:

typescript
const entry = await auditService.getById('12dc5b9e-8e3a-4ec1-b211-f728f924db0f', {
  tenantId: 'tenant-1',
});

Use allTenants: true only after an application-level authorization check.

Released under the MIT License.