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:
| Field | Type | Description |
|---|---|---|
id | string | Unique record identifier. Generated on insert. |
createdAt | number | Epoch milliseconds when the record was created. |
updatedAt | number | Epoch milliseconds when the record was last modified. |
createdBy | string | Identifier of the user or context that created the record. |
updatedBy | string | Identifier of the user or context that last modified the record. |
isArchived | boolean | Soft-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 objectAll date type fields in your schema follow the same convention -- ISO 8601 strings are accepted as input and converted to epoch milliseconds for storage.