
Give every incoming value a clear contract
A form and an API integration can send data in different ways, but they should agree on what a valid record means. Wildo builds standard operation inputs from the resource definition, so required values, length limits, choices and nested structures have a common basis.
When an incoming value does not fit the operation’s contract, the API returns a validation error with the affected field path. The application can show that error beside the input, helping a person correct the value without guessing.
The same model supports different actions. Creating a task needs its initial values; updating it can send just the values that changed. Each action gets an input shape appropriate to its purpose.
Example — Explain why a task cannot be saved
A task needs a title, accepts a bounded description and records progress as an integer between zero and one hundred. An empty title or a progress value of 140 fails the declared input contract. The form can point to that value while the API integration receives the same field-specific information.
For engineers
Start with the data the application accepts
This excerpt from Wonder Todos’ todos.schemas.ts contains the title, description, choices, dates, tags and progress definition. Source comments are omitted, and the surrounding schema continues beyond the excerpt:
title: z.string().min(1).max(200).isSummaryField(),
description: z.string().max(1000).optional(),
status: z.enum(Todos_Status).default(Todos_Status.PENDING).isSummaryField(),
priority: z.enum(Todos_Priority).default(Todos_Priority.MEDIUM).isSummaryField(),
recurringType: z.enum(Todos_RecurrenceType).default(Todos_RecurrenceType.ONE_TIME).isDiscriminator(),
dueDate: z.date().optional().isSummaryField(),
tags: z.array(z.string().min(1).max(40)).optional(),
progressPercent: z.number().int().min(0).max(100).default(0),
snoozedUntil: z.date().nullish(),
The title’s length bounds, the tags’ element constraints and the progress range are part of the schema. optional() permits omission; nullish() also admits an explicit null. Enum-backed fields carry the application’s named choice vocabulary.
Derive a contract for the action
The DTO builder derives an operation’s input from the resource schema and selected variant. Synthesized CREATE inputs omit server-owned fields. UPDATE inputs express a patch: omitted values remain unchanged, while an explicit null clears a field that allows null. A schema family contributes the appropriate variant fields.
An operation may instead declare a purpose-specific requestDto. That is an explicit input contract with its own authoring responsibility; it is not an invitation for clients to choose arbitrary stored fields. The engine records whether that contract was authored rather than synthesized.
Distinguish an omitted value from a cleared value
For the fields above, these illustrative request fragments have different meanings. They are excerpts of a request, not complete bodies for every resource variant:
| Input fragment | CREATE | UPDATE patch |
|---|---|---|
title omitted | Rejected: the title is required. | Leaves the existing title unchanged. |
{ "title": "" } | Rejected by the minimum length. | Rejected by the same constraint. |
description omitted | Accepted as an optional field. | Leaves the existing description unchanged. |
{ "snoozedUntil": null } | Accepted by nullish(). | Explicitly clears the field. |
An omitted PATCH field is not a request to reapply its schema default. This distinction lets a small edit change one value without resetting other fields on the record.
Validate at the API boundary
The controller assembles body or collection-query values, applies network-boundary coercion such as date-string conversion, then parses against the operation request. Validation failures carry structured issues and field paths. Unknown fields in standard synthesized write inputs are stripped; valid declared values continue through authorization and execution.
The form infrastructure uses schema validation locally and can inject server errors into the matching field. Cross-field conditions and stored-data questions still have their appropriate homes: uniqueness is enforced when writing, and a referenced row’s existence or visibility is checked against the actual resource context.
For advance feedback about a name already in use, see live field validation.