Fields
Control whether individual fields can be read or written — enforced on the API and reflected in the admin panel.
Field access control lets you protect a single field instead of the whole document. You can hide an internal note from everyone except admins, or make a slug editable only while a document is being created. You set this with read and update rules on a field's access key.
Field rules are enforced in two places at once:
- On the API — a read-denied field is stripped from responses, and a write-denied field is dropped from the payload before the document is saved.
- In the admin panel — the same rules hide a read-denied field and render a write-denied field read-only.
So field access is a real server-side boundary, not just a display hint. Read the Access Control overview for the shared model, then use this page for the field-specific behavior.
For self-hosted Dyrected, field access can also use function rules when a field decision needs local runtime code. Use Jexl strings when the same behavior should stay visible in the admin form.
The shape
Add an access object with read, create, and/or update to any field. Use a Jexl string for a conditional rule, or the literal false to always apply the restriction:
{
name: 'internalNotes',
type: 'textarea',
access: {
read: "'admin' in user.roles",
update: "'admin' in user.roles",
},
}Omit a rule (or set it to true) to leave the field fully available.
Field rules also accept functions and named policies. Functions (and function-based policies) run on the server only — the admin panel cannot execute them, so it will not hide or lock the field based on one. Jexl strings, false, and string-based named policies are enforced in both places: the server evaluates them, and the admin inlines them into the form. For a field rule that should behave the same in the form and on the API, use a Jexl string, a string named policy, or false.
What each rule does
Read
read controls whether the field's value is returned. When it fails, the API omits the field from the response and the admin panel hides it from the edit form:
{
name: 'internalNotes',
type: 'textarea',
access: {
read: "'admin' in user.roles", // only admins get this field back
},
}Create
create controls whether the field can be set when a document is created. When it fails, the field is dropped from the create payload. If you do not set a create rule, the field falls back to its update rule on create — so update: false alone blocks writes in both cases.
{
name: 'referenceCode',
type: 'text',
access: {
create: "'admin' in user.roles", // only an admin can set it, and only at creation
update: false, // nobody can change it afterward
},
}Update
update controls whether the field can be written on an existing document. When it fails, the API drops the field from the incoming write (the stored value is unchanged) and the admin panel renders it read-only:
{
name: 'slug',
type: 'text',
access: {
update: '!id', // editable while creating, locked once the document exists
},
}id is only present once a document has been saved, so !id allows the field on create and locks it on every later update — on the API and in the form alike.
What a field rule can see
Field rules are evaluated with the standard access context, plus the form's live values when they run in the admin panel:
| Variable | What it is |
|---|---|
user | The signed-in user |
id | The document's ID — present for existing documents, absent on create |
doc | The stored document (server-side checks) |
data | The incoming write payload (server-side checks) |
| (field values) | The current form values, in the admin panel |
Because id is resolved on the server too, !id and user.id != id behave the same whether the request comes from the admin panel or straight from the API.
A field rule should evaluate to a boolean. Row-level filter objects, which collections use to scope a query, do not apply to a single field, so an object result is treated as a denial.
Rules apply at every level of nesting. A read or update rule on a field inside a group, array, or blocks field is enforced on that nested value too.
A realistic example
The scaffolded auth collection uses field access on its roles field so an admin can change other people's roles but not their own — which prevents self-elevation:
{
name: 'roles',
type: 'select',
options: [
{ value: 'admin', label: 'Admin' },
{ value: 'editor', label: 'Editor' },
{ value: 'viewer', label: 'Viewer' },
],
access: {
// an admin editing someone else's record — but not their own
update: "'admin' in user.roles && user.id != id",
},
}A non-admin never gets an editable roles field, and an admin cannot change their own roles — and because it is a Jexl string, that rule holds in the admin form and on a direct API PATCH. An attempt to set roles that fails the check is dropped before the document is saved.
When to reach for hooks instead
Field access answers a yes/no question: can this field be read or written? Reach for a field hook when you need to do more than allow or deny:
- raise a custom error instead of silently dropping the value
- transform the incoming value
- compare several fields and rewrite the data
- trigger a side effect when a forbidden change is attempted
Related pages
- Access control overview — the model behind these rules.
- Collection access control — whole-document rules.
- Field configuration — the options every field accepts.
- Field hooks — server-side logic per field.