Views
A view is a schema class created from an entity:
/** What an author may write. */
export class PostDraft extends Post.pick('slug', 'title', 'summary', 'body') {}
/** What the public index shows — no body. */
export class PostCard extends Post.pick('id', 'slug', 'title', 'summary', 'publishedAt') {}
/** Input of a custom read operation. */
export class BySlugInput extends Post.pick('slug') {}
A view is a full schema class: it has getFields(), validate(), and is usable as a
TypeScript type. Views chain: Post.pick(…).partial().
The four derivations
| Derivation | Produces |
|---|---|
Post.pick('a', 'b') | only those fields |
Post.omit('a') | all fields but those |
Post.partial() | every field optional: an absent field is not updated |
Post.extend({ extra: text() }) | entity plus new fields (how User extends AuthUser) |
The partial mode is retained when the view is used as an operation input.
Where views plug in
- Handler inputs — declare the view as the parameter type; the façade validates the wire body against it before your code runs (Handlers).
- Handler outputs —
Crud(Post, { list: PostCard })names one op's view;Crud(Post, PostPublic)scopes the whole handler (Handlers). - Forms —
useFormFor(PostDraft)derives its fields from the view (Forms).
The io projections
The boundary axis derives two field sets used by every surface:
inputFields(Post.getFields()) // fields a client may WRITE — readOnly excluded
outputFields(Post.getFields()) // fields a client may READ — writeOnly excluded
This is why a form built on Post does not show status or publishedAt inputs, without
additional per-form configuration.
Choosing between a field and a view
readOnly / writeOnly and pick / omit can all remove a field from a surface, but
their scope differs.
- boundary (on the field) defines a global rule. With
writeOnly(password), the password is excluded from every output. - pick / omit (on a view) selects fields for a specific use. For example,
Post.pick('id', 'title')defines the public index response.
The two mechanisms are complementary.
- Without
writeOnlyon the field, every output view would need to omitpassword. The global rule prevents that omission. - A public response (
id,title) and an admin response (+ authorEmail) instead need different views:authorEmailis included or excluded depending on the use.
Choose based on the rule's scope: global or specific to one use.
| The fact is… | It lives… | Example |
|---|---|---|
| true everywhere (invariant) | on the field — readOnly / writeOnly | a password never leaves |
| true for one use (local) | in a derivation — pick / omit | this endpoint returns only id + title |
A boundary rule is serialized in the card under
x-fougere.boundary, so a consumer in another language can apply it. A view derived in
TypeScript is not serialized.
Next: The identity card — the schema's portable form.