Browse all documentation

Virtual field

Return a typed value computed by trusted Go code without accepting or storing it.

Use field.Virtual when trusted Go code derives a response value from a document or another access-controlled read. Virtual values appear in generated output contracts but are never accepted as create/update data and are not stored.

In the admin

A read-only Computed label virtual field in the Ridu admin showing a value resolved by Go code.

The admin renders resolver output as read-only; create and update inputs never accept or persist it.

Define the field and resolver

content/people.go
package content

import (
  "github.com/riducms/ridu"
  "github.com/riducms/ridu/field"
  "github.com/riducms/ridu/store"
)

var People = ridu.Collection{
  Slug: "people",
  Fields: []field.Definition{
    field.Text("firstName", field.Required()),
    field.Text("lastName", field.Required()),
    field.Virtual("displayName", field.ValueString),
  },
  Computed: map[string]ridu.Computed{
    "displayName": func(ctx ridu.ComputedContext) (store.Value, error) {
      first, _ := ctx.Document.Values["firstName"].StringValue()
      last, _ := ctx.Document.Values["lastName"].StringValue()
      return store.String(first + " " + last), nil
    },
  },
}

Read sibling values

Virtual fields are root fields, so their sibling values live in ctx.Document.Values. In the example, displayName reads the stored firstName and lastName siblings with StringValue(). The second return value reports whether the value is actually a string; the example can ignore it because both source fields are required Text fields.

Use BooleanValue() or NumberValue() for other scalar siblings, ObjectValue() for a Group or named Tabs value, and Values() for an Array or Blocks value. A Relationship or Upload sibling contains its stored reference shape—an ID, ID list, or polymorphic object—not an automatically populated document. Use ctx.Local when the resolver also needs to read that related document through its normal access rules.

Choose ValueString, ValueNumber, ValueBoolean, or ValueJSON. The resolver must return the matching store.Value. Missing resolvers fail startup; a mismatched runtime value fails the operation.

Virtual fields must live at a collection or global root. Resolvers receive the operation, actor and auth collection, current document, locale selection, context, and access-controlled Local API. Use that Local API for additional reads.

Cost, access, and selection

Unselected virtual fields do not run. Consumers should select only expensive computed output they need. A resolver is trusted application code, so bound its work and avoid network calls on every list row unless caching and failure behavior are defined. The output remains subject to field read visibility.

Common mistakes

  • Do not try to filter or sort by a value that is never stored. Materialize/index a real field when the database must query it.
  • Avoid nondeterministic or secret-bearing output unless an access rule permits it.
  • A Virtual is not an admin-only UI element; use UI for presentation with no response value.

See field.Virtual and ridu.Computed.