When one model must serialize differently for a list endpoint and a detail endpoint, how do serialization views or groups work?
answer
- one model, several named shapes
- fields carry view tags
- the endpoint picks the active view
- untagged fields: check the default
- does the view reach nested objects
basics
~20 sFields on the model are tagged with one or more view names, and each endpoint declares which view is active; the mapper then writes only the fields carrying that tag. The server chooses the view, not the caller.
solid answer
~50 sA view (some frameworks call it a group or a scope) is a named subset of a type's fields. You tag each property with the view names it belongs to — `summary`, `detail`, `admin` — and the endpoint declares which view its serialization runs under, usually as metadata on the handler or by passing the view to the write call. The mapper filters the property set against that name before writing. This lets a list endpoint emit a thin item and a detail endpoint emit the full object from one model, without a second type per shape. Two things to pin down: what happens to a property tagged with *no* view, which differs between frameworks, and whether the active view propagates into nested objects. Computed output fields are tagged the same way, which matters because a derived value tagged into a list view runs once per item.
go deeper
Learn the idea first: fields are labelled, the endpoint picks a label, and the mapper writes only the labelled fields. That is how one type serves both a list and a detail response.
Explain the filtering step inside the mapper, the untagged-field policy, and whether the view carries into nested objects and collection elements. Know that the server, not the caller, selects the view.
Bring the operational angle: measure what computed fields cost per item in list views, pin the untagged default with a test, and recognise when a view set has grown into something a purpose-built response type would express more safely.
Decide the house rule. Views keep the type count low and the shapes coupled; response types per endpoint cost more code but make every shape reviewable in one place and make new domain fields inert until someone maps them.
## One model, two representations A collection endpoint and a single-resource endpoint usually want different amounts of the same thing. The list wants an identifier, a title, maybe a status; the detail wants the description, the timestamps, the nested relations. Writing two unrelated types is one answer. **Views** (also called *groups*, *scopes* or *serialization contexts*) are the other: one type, whose properties are labelled, plus a per-endpoint choice of which labels are active. The key property of this mechanism is that **the server decides**. The view is fixed by the endpoint at build time, so the set of shapes an API can produce is finite, reviewable and documentable — unlike caller-driven field selection, which is a different concern entirely. ## How selection actually works 1. **Tag the fields.** Each property carries the names of the views it belongs to. A property may belong to several. 2. **Declare the active view at the endpoint.** Either as declarative metadata attached to the handler, or by handing the view name to the serialization call along with the object. 3. **The mapper filters, then writes.** When the mapper enumerates properties it consults the active view and skips anything not tagged with it. Nothing about the object changes; only what is written changes. Because step 3 happens inside the mapper, the same object instance can be written two ways in the same process with no copying and no conditional code in the handler. ## The two questions that decide whether it is safe **What about a property with no tag at all?** Frameworks differ here, and this is the single most common source of surprise: in some, an untagged property is considered part of every view and is always written; in others, selecting a view means *only* tagged properties survive, and untagged ones vanish. The difference flips the default from leak-prone to drop-prone. Whichever your mapper does, write a test that pins it, because the consequence of the first policy is that a newly added, untagged property appears in your thinnest public list response. **Does the view reach nested objects?** When the mapper recurses into a nested object or into the elements of a collection, the active view may or may not carry down. If it does not, the nested type is written in full under a summary view, and the "thin" list quietly embeds a fat object. ## Computed output fields Not every written field is stored. A **computed output field** is produced during serialization — a display name assembled from parts, a count, a derived status flag, a boolean saying whether the current caller may edit the item. These are tagged into views exactly like stored fields, and they carry two costs worth naming in an interview: - **Per-item cost.** A computed field tagged into a *list* view runs once per element. If the computation touches storage, a page of fifty items is fifty extra round trips. - **Caller dependence.** A computed field whose value depends on who is asking makes the response body caller-specific, which constrains any shared caching of that body downstream. ## Views versus a type per shape | | One model with views | A model per response shape | |---|---|---| | Number of types | One | One per shape | | Where the shape is visible | Scattered across field tags | Readable in one type | | Risk of a new field leaking | High if untagged means "always written" | Low: the field must be added deliberately | | Cost of a third shape | A tag pass over the type | A new type plus mapping | Views pay off while the shapes are genuinely nested subsets of one another — summary inside detail inside admin. They stop paying when the shapes start diverging in structure rather than in field count, because a tag cannot rename a key, flatten a nested object, or change a type. ## Interview signal Name the mechanism (tagged fields plus a per-endpoint active view), state that the server owns the choice, and volunteer the two hazards without being asked: untagged-field policy and nested propagation. Mentioning the per-item cost of computed fields in list views marks someone who has actually shipped one.
- What happens to a property that carries no view tag when a view is active?It depends on the framework's policy, which is exactly why it needs pinning in a test. Some treat untagged properties as belonging to every view and always write them; others write only tagged properties once a view is selected. The first policy leaks new fields into thin responses; the second silently drops fields nobody remembered to tag.
- Why can a computed output field be expensive in a list view but cheap in a detail view?It is evaluated once per serialized object. In a detail response that is one evaluation; in a page of items it is one per item. If the computation reads storage or calls another service, the list endpoint turns into a per-row fan-out that does not show up in the handler's code at all.
- When do views stop being the right tool for a response shape?When the shapes differ structurally rather than by field count. A view can include or omit a property, but it cannot rename a key, flatten a nested object, change a value's type, or merge two properties into one. Once an endpoint needs any of that, a purpose-built response type is clearer than an increasingly clever tag set.
saying these in an interview costs you the question
- Thinks the client chooses the view through a query parameter
- Assumes an untagged field behaves the same in every framework
- Expects the active view to always propagate into nested objects
- Adds a view tag per endpoint until the combinations are unreadable
- Ignores that a computed field in a list view runs once per item