One page per field type, generated from the field classes themselves: their TSDoc, their builder methods, the detail projection and table cell they declare, and the Vine base they contribute. Descriptor protocol v1.
Fields are the state-bearing nodes of the schema tree. Each page covers what its type adds; the DSL every field shares — chrome, projection, state, validation, reactivity — is below.
Field types
| Type key | Builder | Detail projection | Column |
|---|---|---|---|
text-input |
F.text |
text-entry |
text |
textarea |
F.textarea |
text-entry |
text |
number |
F.number |
text-entry |
number |
slider |
F.slider |
text-entry |
number |
select |
F.select |
text-entry |
text |
radio |
F.radio |
text-entry |
text |
checkbox |
F.checkbox |
text-entry |
boolean |
toggle |
F.toggle |
text-entry |
boolean |
checkbox-list |
F.checkboxList |
text-entry |
text |
date |
F.date |
text-entry |
date |
datetime |
F.datetime |
text-entry |
date |
time |
F.time |
text-entry |
date |
color |
F.color |
text-entry |
text |
hidden |
F.hidden |
text-entry |
— |
repeater |
F.repeater |
repeater-entry |
— |
key-value |
F.keyValue |
key-value-entry |
— |
json |
F.json |
code-entry |
— |
code |
F.code |
code-entry |
— |
markdown |
F.markdown |
text-entry |
text |
rich-text |
F.richText |
html |
— |
file |
F.file |
text-entry |
text |
image |
F.image |
image-entry |
image |
belongs-to |
F.belongsTo |
relation-entry |
— |
belongs-to-many |
F.belongsToMany |
relation-entry |
— |
has-many |
F.hasMany |
repeater-entry |
— |
morph-to |
F.morphTo |
relation-entry |
— |
The projection column shows a DEFAULT instance. Choice fields decide per instance —
a select whose options carry colours projects to badge-entry and derives a badge
cell; its own page says so.
The shared field DSL
Every field inherits these from Field and SchemaComponent
(packages/core/src/schema/). Listed once here instead of on all 26 type pages.
| Method | Declared on | Behaviour |
|---|---|---|
label(text: string): this |
Field |
Human label rendered above the input / beside the entry. |
helper(text: string): this |
Field |
Helper text rendered under the input. |
placeholder(text: string): this |
Field |
Input placeholder. |
prefix(text: string): this |
Field |
Static text rendered inside the control, before the value. |
suffix(text: string): this |
Field |
Static text rendered inside the control, after the value. |
withProps(extra: JsonObject): this |
Field |
Merges arbitrary JSON-safe props onto the wire node (§7.3 custom fields). The escape hatch for props the typed DSL does not model; props contents are the protocol’s open, additive-only surface (protocol §10). |
visibleOn(...contexts: FieldContextName[]): this |
Field |
Restricts the field to the listed rendering contexts (§7.2). Replaces the previous allowlist. If Field.hiddenOn also names a context, the denylist wins; both are evaluated by Field.appearsIn. |
hiddenOn(...contexts: FieldContextName[]): this |
Field |
Replaces the denylist of rendering contexts in which the field is removed (§7.2). |
readonlyOn(...modes: FormMode[]): this |
Field |
Renders the field read-only in the listed form sub-modes (§7.2). A read-only field still renders and still validates, but is excluded from the fill set for that mode (§19: the value cannot be tampered in). |
disabled(): this |
Field |
Statically disables the control (never fillable, never required). |
virtual(): this |
Field |
Marks the field virtual (§7.2): it participates in the descriptor and validator but is NEVER filled onto the model. Its pruned value remains available to BaseResource.beforeSave and BaseResource.afterSave, where the resource may persist it. |
sensitive(): this |
Field |
Marks the field’s value a SECRET (TECH_SPEC §13 payload “secrets- scrubbed: fields may mark sensitive()”; §19). |
default(value: TValue): this |
Field |
Default state value in create mode (protocol §7). |
dehydrate(fn: DehydrateFn<TValue>): this |
Field |
Custom form-state → model-attribute mapping (§7.2). |
hydrate(fn: HydrateFn<TValue>): this |
Field |
Custom model-record → form-state mapping (§7.2). |
required(when?: { field: string; matcher: ReactiveMatcher }): this |
Field |
Marks the field required (§11.1). With a when matcher it compiles to a requiredWhen reactive rule instead, mirrored server-side (§11.2). |
nullable(): this |
Field |
Allows null as a valid submitted value (§11.1). |
unique(opts: UniqueSpec = {}): this |
Field |
Compiles to Vine’s DB unique rule (§11.1) with automatic whereNot(primaryKey, currentId) in edit mode and tenant scoping when tenancy is active (§5.3). |
rules(fn: RulesFn): this |
Field |
Replaces this field type’s Vine base with a user-supplied schema (§7.2). |
maxLength(limit: number): this |
Field |
Maximum string length; compiles to Vine maxLength (§11.1). |
minLength(limit: number): this |
Field |
Minimum string length; compiles to Vine minLength (§11.1). |
visibleWhen(condition: ReactiveCondition): this |
Field |
Declarative visibility rule (TECH_SPEC §7.2, the reactivity contract). Client-side the rule is cosmetic; the validator mirrors it server-side (§11.2). |
requiredWhen(condition: ReactiveCondition): this |
Field |
Declarative requiredness rule; mirrored in the compiled validator even when the client UI is bypassed (the server-mirroring contract). |
disabledWhen(condition: ReactiveCondition): this |
Field |
Declarative disabledness rule (the reactivity contract). A disabled field is never required and never filled (mirroring rule R5). |
sets( target: string, transform: SetsTransform, options: { source?: string; if?: JsonLogicRule } = {} ): this |
Field |
Appends a declarative state derivation run when this field changes (protocol §5 sets), e.g. .sets('slug', 'slugify'). Derivations execute in declaration order. |
live(debounceMs: number = 300): this |
Field |
Marks the field “live”: dependent reactive rules re-run after local edits, debounced (protocol §5). Defaults to a 300 ms debounce. |
dependentOptions(resolver: OptionsResolver, dependsOn: string[] = []): this |
Field |
Server-resolved dependent options (§7.2): the client refetches through this node’s urls.options capability URL, sending the dependsOn state slice (protocol §4/§5). |
canSee(fn: CanSeeCallback): this |
SchemaComponent |
Compile-time visibility gate (TECH_SPEC §7.2). When the callback returns false for the current request, the node is omitted from the compiled descriptor and its state key is stripped in the same pass. |
requiresAbility(ability: ResourceAbility): this |
SchemaComponent |
Ability gate (TECH_SPEC §12, descriptor enforcement point): the node is OMITTED whenever the current request does not hold ability on the resource being compiled. |
visible(fn: CanSeeCallback): this |
SchemaComponent |
Spelling of SchemaComponent.canSee used by the §7.1 layout DSL. Identical semantics — compile-time omission, never a visible: false flag on the wire (protocol §2 rule 3), and equally additive. |
recordDependent(): this |
SchemaComponent |
Marks this node’s subtree as recompiled per request (VisibilityScope request), never served from the §9.4 descriptor cache. |
columnSpan(span: number | 'full'): this |
SchemaComponent |
Width of this node inside a columns(n) layout parent; a columnSpan equal to the parent’s columns (or the string 'full') spans the row (protocol §2 rule 5). |
Introspection
Read by the descriptor compiler, the validator compiler and the table derivation. Useful in tests; rarely called from a schema.
| Member | Declared on | Behaviour |
|---|---|---|
get displayType(): string |
Field |
Display counterpart of THIS instance (§7.4, protocol §8). Mirrors the class’ static displayType — the SchemaComponent.type pattern — but as an instance member, because the projection of a choice field is a per-instance decision: select/radio/checkbox-list project to badge-entry only when their declared options carry colours, and to text-entry otherwise. The descriptor compiler reads THIS, never the static. |
detailProps(): JsonObject |
Field |
Props THIS field’s display counterpart needs in detail mode (protocol §8), as a fresh JSON-safe object. |
get columnType(): string | undefined |
Field |
Cell counterpart of THIS instance (§7.5), undefined when the field is not tabulatable. Instance-level for the same reason as Field.displayType: a coloured choice field derives a badge cell, an uncoloured one a text cell. Table derivation reads THIS. |
get columnKey(): string |
Field |
Key of the index column this field derives to (§7.5), when it derives one at all. Defaults to the state key, which is right for every field whose value IS the column’s value. |
get capabilities(): readonly string[] |
Field |
Capability keys this field needs as urls entries on its wire node (protocol §4): options for anything whose option list the client has to ASK the server for, upload for the media types. Empty for a plain scalar field — a node with nothing dynamic to fetch carries no urls block at all, since optional protocol keys are never serialized as empty. |
contributeHints(_hints: EagerHints): void |
Field |
Registers what this field needs eager-loaded before a detail/edit page reads it (§18), mirroring Column.contributeHints on the table side. A scalar field needs nothing; a relation field registers its relation, so hydrating tags into an id array costs zero per-record queries. |
appearsIn(context: FieldContextName): boolean |
Field |
Whether this field appears in context under the static projection rules (§7.4). Independent of canSee, which is the request-dependent gate on SchemaComponent. |
isReadonlyIn(mode: FormMode): boolean |
Field |
Whether the field renders read-only in the given form sub-mode (§7.2). |
get visibleContexts(): readonly FieldContextName[] | undefined |
Field |
Explicit visibleOn contexts, when declared. |
get hiddenContexts(): readonly FieldContextName[] | undefined |
Field |
Explicit hiddenOn contexts, when declared. |
get isVirtual(): boolean |
Field |
Whether the field is virtual (never filled, §7.2). |
get isSensitive(): boolean |
Field |
Whether the value is a secret (see Field.sensitive). |
nestedSchema(): readonly SchemaComponent[] | undefined |
Field |
The nested sub-form of a state-boundary field (§7.3 repeater), or undefined for every scalar field. |
boundaryKeys(): readonly string[] |
Field |
Keys a state boundary owns ITSELF, as opposed to keys contributed by its item sub-form (§7.3). Empty for every field but belongs-to-many, whose items carry the relation key beside the pivot fields. |
get dehydrator(): DehydrateFn<TValue> | undefined |
Field |
The declared dehydrate mapper, when any. |
get hydrator(): HydrateFn<TValue> | undefined |
Field |
The declared hydrate mapper, when any. |
hydrateValue(record: unknown, ctx: HttpContextLike): TValue |
Field |
Maps record onto this field’s form state (§7.2): the author’s hydrate() when declared, otherwise the field type’s Field.defaultHydrate. This is the only entry point state builders should call — reading the attribute directly skips every per-type conversion. |
dehydrateValue( value: TValue, ctx: HttpContextLike, record?: unknown ): unknown | Promise<unknown> |
Field |
Maps a validated form value onto the model attribute (§7.2): the author’s dehydrate() when declared, otherwise the field type’s Field.defaultDehydrate. May be async — a user mapper is allowed to await (dehydrate: (value, ctx) => unknown | Promise), so the fill pipeline awaits the result. |
get isNullable(): boolean |
Field |
Whether nullable() was declared. |
get uniqueSpec(): UniqueSpec | undefined |
Field |
The declared unique constraint, when any. |
buildValidation(options: ValidationBuildOptions): SchemaTypes |
Field |
Builds this field’s Vine schema for one submission (§11.1). Called by the validator compiler, which owns optionality/grouping and never branches on field classes. |
get optionsResolver(): OptionsResolver | undefined |
Field |
The declared dependent-options resolver, when any. |
get refetchSchemaCauses(): readonly string[] |
Field |
The DSL calls that forced refetchSchema: true on this field, in declaration order (e.g. visibleWhen('status', <closure>)). |
get reactiveSpec(): ReactiveSpec | undefined |
Field |
The declarative reactive spec for this field, or undefined when no reactive behavior was declared (the reactive key is then omitted from the wire node entirely — optional keys are never serialized as empty). |
get type(): string |
SchemaComponent |
Registry key of this node on the wire (mirrors the static type). |
get visibilityScope(): VisibilityScope |
SchemaComponent |
How this node’s visibility relates to the §9.4 cache key — read by the descriptor compiler to decide whether the subtree can be memoized. The STRONGEST scope among the declared gates wins, so a node can only ever move further away from the cache as restrictions are added. |
maySee(ctx: HttpContextLike, record?: unknown): boolean |
SchemaComponent |
Whether this node survives compilation for ctx (and record in edit/detail modes) — true only when EVERY declared gate passes. |
get props(): JsonObject |
SchemaComponent |
Snapshot of the accumulated props (never undefined on the wire). |
Coverage in the example app
From the committed codegen manifest of examples/blog-admin
(.adonisjs/adonia/manifest.json, TECH_SPEC §14). The generator asserts its
protocolVersion against the runtime constant, exactly as adonia:doctor does.
| Resource | Class | Panels | Schema fields | Index columns |
|---|---|---|---|---|
activity-log |
ActivityLogResource |
admin, domain-admin |
action, actorId, actorType, batchId, changes, createdAt, exception, panel, payload, resource, status, targetId, updatedAt |
action, actorId, changes, createdAt, resource, status |
categories |
CategoryResource |
admin, domain-admin |
name, slug |
name, posts, slug |
comments |
CommentResource |
admin, domain-admin, tenant-admin |
body, userId |
body, userId |
courses |
CourseResource |
admin, domain-admin |
description, title |
lessons, title |
lessons |
LessonResource |
admin, domain-admin |
content, position, title |
position, title |
posts |
PostResource |
admin, domain-admin, tenant-admin |
authorId, body, categoryId, coverImage, publishedAt, slug, status, subtitle, tags, title |
author.fullName, comments, publishedAt, slug, status, tags.name, title |
tags |
TagResource |
admin, domain-admin |
name, slug |
name, slug |
users |
UserResource |
admin, domain-admin, tenant-admin |
email, fullName, role |
email, fullName, role |