DataFn
Concepts

System Fields

Built-in fields managed automatically by DataFn.

Overview

Every resource in DataFn automatically includes a set of system-managed fields. These fields are reserved names -- you do not declare them in your schema, and they cannot be overridden by user-defined fields.

SYSTEM_FIELDS

The following fields exist on every record:

FieldTypeDescription
idstringUnique record identifier. Generated on insert.
createdAtnumberEpoch milliseconds when the record was created.
updatedAtnumberEpoch milliseconds when the record was last modified.
createdBystringIdentifier of the user or context that created the record.
updatedBystringIdentifier of the user or context that last modified the record.
isArchivedbooleanSoft-delete flag. false by default.
import { SYSTEM_FIELDS } from "@datafn/core";

// Set{"id", "createdAt", "updatedAt", "createdBy", "updatedBy", "isArchived"}
console.log(SYSTEM_FIELDS);

READONLY_SYSTEM_FIELDS

A subset of system fields that are server-managed and cannot be written by clients:

import { READONLY_SYSTEM_FIELDS } from "@datafn/core";

// Set{"createdAt", "updatedAt", "createdBy", "updatedBy"}
console.log(READONLY_SYSTEM_FIELDS);

These four fields are set automatically by the server on every insert and update. Any attempt to include them in a mutation payload is rejected.

ID Generation

Record IDs are generated automatically on insert. When a resource defines an idPrefix, the generated ID uses that prefix:

{
  name: "tasks",
  version: 1,
  idPrefix: "tsk",
  fields: [...]
}

This produces IDs in the format tsk_<random>, for example tsk_a1b2c3d4. The prefix makes it easy to identify which resource a record belongs to when inspecting raw data.

If no idPrefix is specified, a plain random ID is generated without a prefix.

isArchived and Soft Delete

The isArchived field is intentionally not included in READONLY_SYSTEM_FIELDS. Clients can set and clear it directly, which makes it the built-in mechanism for soft deletes.

// Soft-delete a record
await client.mutation({
  resource: "tasks",
  action: "merge",
  id: "tsk_a1b2c3d4",
  data: { isArchived: true },
});

// Restore a soft-deleted record
await client.mutation({
  resource: "tasks",
  action: "merge",
  id: "tsk_a1b2c3d4",
  data: { isArchived: false },
});

The @datafn/client package also provides a createSoftDeletePlugin that automatically filters out archived records from query results and converts delete mutations into archive operations:

import { createSoftDeletePlugin } from "@datafn/client";

const client = createDatafnClient({
  schema,
  plugins: [createSoftDeletePlugin()],
  // ...
});

Date Storage

The createdAt and updatedAt fields store dates as epoch milliseconds (Unix timestamp in ms). DataFn provides utility functions for conversion:

import { toEpochMs, fromEpochMs } from "@datafn/core";

// Convert a Date to epoch milliseconds
const epoch = toEpochMs(new Date("2025-01-15T10:30:00Z"));
// => 1736934600000

// Convert epoch milliseconds back to a Date
const date = fromEpochMs(1736934600000);
// => Date object

All date type fields in your schema follow the same convention -- ISO 8601 strings are accepted as input and converted to epoch milliseconds for storage.