Dyrected
Editor Experience

Audit History

Turn on audit logging with one flag to record every create, update, and delete on a collection — with a before-and-after snapshot and the acting user — then read that history from the SDK or the admin.

Audit logging keeps a durable record of who changed what. Turn it on for a collection and Dyrected writes an entry for every create, update, and delete, capturing a before-and-after snapshot and the acting user — so later you can answer "who changed this, and what did it look like before?" without guessing.

Reach for it on collections where changes need accountability: content under compliance review, records that support operational auditing, or anything where a reliable history of edits matters. By the end of this page you'll know how to enable it, what each entry contains, how to read the trail from your code or the admin, and how to control who's allowed to see it.

Audit logging is different from runtime logging. Runtime logs help you monitor and debug the running service. Audit logs are durable content-history records stored in __audit. If you need request logs, redaction, tracing, or metrics, use Logging and Observability.

Enabling audit

Set audit: true on any collection. That's the whole setup — Dyrected handles the logging for you.

import { defineCollection, defineTextField } from "@dyrected/core";

export const Orders = defineCollection({
  slug: "orders",
  audit: true,
  fields: [
    defineTextField({ name: "reference", label: "Reference", required: true }),
    defineTextField({ name: "status", label: "Status", required: true }),
  ],
});

The first time any collection turns on audit, Dyrected registers a hidden __audit collection to store the entries. You don't define or manage it yourself, and it stays out of the admin sidebar.

What gets logged

Each entry records:

  • the collection slug and document id the change applied to
  • the operationcreate, update, or delete
  • the acting user's id, when the request is authenticated
  • a timestamp
  • the change itself, as a before-and-after snapshot (before is empty on create, after is empty on delete)

Audit writes run in the background and never block the operation being logged. If a log write fails, the original create, update, or delete still succeeds — the trail is best-effort by design, so it can never break a real request.

Reading the log

The __audit collection is locked down — you don't query it like a normal collection. Instead, read entries through two dedicated SDK methods, depending on the scope you want.

For a single collection — for example, the full history of one order:

const { docs } = await client.collection("orders").audit({
  where: { documentId: { equals: orderId } },
  limit: 50,
});

Or across every audited collection the caller is allowed to read — useful for an activity feed:

const { docs } = await client.audit({ limit: 100 });

Both return a paginated list of entries, newest first. Each entry has this shape:

interface AuditEntry {
  id: string;
  collection: string;
  documentId: string | null;
  operation: string;      // "create" | "update" | "delete"
  user: string | null;    // the acting user's id
  timestamp: string;
  changes?: string | Record<string, unknown> | null; // { before, after }
}

changes holds the before-and-after pair. Compare the two sides yourself to show exactly which fields moved — that's the same thing the admin does for its diff view below.

In the admin

On a collection with audit enabled, the document editor gains a History panel — the clock icon in the editor header. It lists that document's entries newest first and renders a field-level diff for each change, so a reviewer can see what moved without leaving the admin or writing any query.

Controlling who can read the log

By default, anyone who can read the collection can read its audit trail. To gate the log separately — for instance, so only admins see who changed what — add a readAudit rule to the collection's access:

export const Orders = defineCollection({
  slug: "orders",
  audit: true,
  access: {
    read: true,
    // Only admins can read the audit trail, even though anyone can read orders.
    readAudit: "user.roles && 'admin' in user.roles",
  },
  fields: [
    defineTextField({ name: "reference", label: "Reference", required: true }),
    defineTextField({ name: "status", label: "Status", required: true }),
  ],
});

readAudit falls back to the collection's read rule when you leave it off, so you only set it when the audit trail needs to be more restricted than the documents themselves. It accepts the same forms as any other access rule — true/false, a Jexl expression, or a named policy.

On this page

Dyrected| Cloud

Get your backend ready in minutes

Use a managed database, storage, APIs, and admin dashboard without setting up the infrastructure yourself.

Set Up My Backend