IPA-124: Repeated Fields
Providing clients with lists of data can be complex. Aligning on a single strategy for providing clients with repeated fields allows the complexity to be reduced when clients need to modify the lists.
Guidance
Repeated fields must use a plural field name.
components:
schemas:
Project:
type: object
properties:
tags:
type: array
items:
type: stringWhy:A plural name signals at a glance that the field holds many values, so a reader expects an array before reading the type.
components:
schemas:
Project:
type: object
properties:
tag:
type: array
items:
type: stringWhy:A singular name on an array field is misleading: the name says one value while the type says many. Naming and cardinality should agree.
For each schema under
components.schemas, list every property whosetypeisarray, including arrays nested inside objects and array items.For each array property, inspect the field name and decide whether it is grammatically plural (
tags,members,items) rather than singular (tag,member,item).Report any array property whose name is singular as a violation.
Repeated fields should have an enforced upper bound that will not cause a single resource payload to become too large. A good rule of thumb is 100 elements.
components:
schemas:
Project:
type: object
properties:
members:
type: array
maxItems: 100
items:
type: stringWhy:maxItemscaps the array, so a single resource payload cannot grow without limit no matter how many members are added.components:
schemas:
Project:
type: object
properties:
members:
type: array
items:
type: stringWhy:With no
maxItems, the array is unbounded and the resource payload can grow arbitrarily large as members accumulate.If repeated data has the chance of being too large, the API should use a sub-resource instead.
paths:
/projects/{projectId}/members:
get:
operationId: listMembers
responses:
"200":
description: A page of members.Why:A potentially large list is exposed as its own collection, so it can be paged through rather than embedded whole in the parent resource payload.
components:
schemas:
Project:
type: object
properties:
members:
type: array
maxItems: 100000
items:
type: stringWhy:The data is expected to grow large, yet it is embedded as an inline array with a huge bound. Every read of the parent resource carries the entire list, which a sub-resource collection would avoid.
For each array property in
components.schemas, estimate the realistic maximum element count from the field name, description, and any documented limits.Decide whether the data can grow large enough that embedding the whole list in every parent payload is a concern (for example, an upper bound well above the 100-element rule of thumb, or growth driven by end-user activity).
For such a property, check whether the list is instead exposed as a paged sub-resource collection under the parent path.
Report any list that can grow large but is embedded inline rather than modeled as a sub-resource.
Depends onClient-owned repeated fields must be respected by the server.
components:
schemas:
Project:
type: object
properties:
labels:
type: array
items:
type: string
description: Client-owned. Stored and returned exactly as supplied.Why:The field is client-owned, and the server stores and returns the array as supplied, so a read returns the same list a write sent.
components:
schemas:
Project:
type: object
properties:
labels:
type: array
items:
type: string
description: Client-owned. The server sorts and de-dupes it.Why:The field is client-owned, but the server quietly reorders and de-duplicates it, so the value read back differs from the value written and a declarative client sees drift.
Identify the repeated fields that are client-owned per IPA-111: those not documented as server-owned or
readOnly.For each, inspect the implementing source or run the create and read operations to observe what the server stores and returns.
Confirm a written list is returned with the same elements, in the same order, with duplicates preserved.
Report any client-owned repeated field that the server silently alters on write or read.
The server must not modify the order of elements or remove duplicates, unless the field is explicitly declared as a set (see List vs Set below).
components:
schemas:
Project:
type: object
properties:
roles:
type: array
x-xgen-array-semantic: set
items:
type: stringWhy:The field is declared a set with
x-xgen-array-semantic, so reordering and de-duplication are part of its contract. A consumer knows order is not significant and treats equality without regard to it.components:
schemas:
Project:
type: object
properties:
roles:
type: array
items:
type: string
description: A list of roles.Why:Absent
x-xgen-array-semantic: set, the array is a list, so the server must preserve order and duplicates. If it reorders or de-duplicates the value at runtime, a declarative client sees drift it cannot anticipate.For each repeated field, send a write with a known element order that includes a deliberate duplicate.
Read the resource back and compare the returned array to the written one for reordering or removed duplicates.
When the list is transformed, confirm the field declares
x-xgen-array-semantic: set.Report any repeated field the server reorders or de-duplicates that is not declared as a set.
List vs Set
Arrays fall into two kinds, and the distinction governs whether order and duplicates are part of the resource state. Classifying each array — and declaring sets explicitly — lets clients and downstream tooling reason correctly about order and equality.
Every array property must be classified as either a list or a set. Set-like arrays must be declared with the
x-xgen-array-semanticextension (see IPA-131); when the extension is absent, the array is treated as a list.- List — order is meaningful and elements may repeat. This is the default when the semantic is not declared.
- Set — order is not meaningful and elements are unique.
components:
schemas:
Project:
type: object
properties:
environments:
type: array
items:
type: string
roles:
type: array
x-xgen-array-semantic: set
items:
type: stringWhy:environmentsis left as a list because order matters, whilerolesdeclaresx-xgen-array-semantic: setbecause it is an unordered, duplicate-free collection. Each array's intent is explicit.components:
schemas:
Project:
type: object
properties:
roles:
type: array
items:
type: string
description: An unordered, duplicate-free set of roles.Why:The description says the field is an unordered set, but it carries no
x-xgen-array-semantic: setdeclaration, so tooling treats it as a list and expects order and duplicates to be preserved.For each array property, determine whether element order is part of the resource state.
Confirm arrays whose order is not meaningful declare
x-xgen-array-semantic: set.Flag any array described as unordered or duplicate-free that omits the set declaration.
For all lists, the server must return elements in a deterministic, stable order across repeated GET responses for the same resource state
- For client-owned lists, the order must be the one the client provided, per the client-owned fields rule above.
- Unstable ordering causes false drift in declarative clients.
components:
schemas:
Project:
type: object
properties:
members:
type: array
items:
type: string
description:
Server-owned list. Returned in a stable order across reads.Why:Repeated reads of the same resource state return
membersin the same order, so a declarative client never sees the list change without an actual change to the resource.components:
schemas:
Project:
type: object
properties:
members:
type: array
items:
type: string
description: Server-owned list. Returned in arbitrary order per read.Why:The server returns
membersin a different order on each read of an unchanged resource, so a declarative client repeatedly detects and tries to reconcile drift that does not exist.For each list field, read the same unchanged resource multiple times.
- Compare element order across the responses.
Report any list whose order varies across reads of the same resource state.
Depends onFor arrays declared as sets, the server must not return duplicate elements
- The server may return elements in any order; the order in a response is not part of the resource state.
- Clients must treat equality without regard to order.
components:
schemas:
Project:
type: object
properties:
roles:
type: array
x-xgen-array-semantic: set
items:
type: string
description:
Set. Returned without duplicates; order is not significant.Why:The set is returned with unique elements, and consumers compare two responses by membership rather than position, so a reordered response is not treated as a change.
components:
schemas:
Project:
type: object
properties:
roles:
type: array
x-xgen-array-semantic: set
items:
type: string
description: Set that may contain repeated elements.Why:The field is declared a set but the server returns duplicate elements, so its contents contradict the set contract and consumers cannot rely on membership semantics.
For each field declared
x-xgen-array-semantic: set, read the resource and inspect the returned array.Confirm the array contains no duplicate elements.
Report any set field whose response includes duplicates.
Depends on
Update Strategy
A resource may use one of two strategies to enable updating a repeated field: direct update using the standard Update method, which can only update the entire list; or custom
AddandRemovemethods.When choosing a custom method approach, the field must not be settable via Create or Update operations.
components:
schemas:
Project:
type: object
properties:
members:
type: array
readOnly: true
items:
type: stringWhy:The repeated field is managed only through the custom add and remove methods, so it is marked
readOnlyand the create and update request bodies cannot set it.components:
schemas:
Project:
type: object
properties:
members:
type: array
items:
type: stringWhy:Custom add and remove methods manage the list, yet the field is also writable through create and update, leaving two competing ways to set it and an ambiguous source of truth.
Determine which repeated fields are managed by custom
AddandRemovemethods by scanning for custom-method operations that target a list.For each such field, locate its property in the create and update request body schemas.
Confirm the field is
readOnlyor absent from those request bodies, so the standard create and update operations cannot set it.Report any custom-method-managed repeated field that remains settable through create or update.
Declarative-friendly resources must use the standard Update method, and not introduce
AddandRemovemethods. To learn more, see IPA-127.paths:
/projects/{projectId}:
put:
operationId: updateProject
requestBody:
content:
application/json:
schema:
type: object
properties:
members:
type: array
items:
type: stringWhy:A declarative-friendly resource updates the whole list through the standard update method, so the desired state is expressed in one place that a declarative client can reconcile.
paths:
/projects/{projectId}:members:add:
post:
operationId: addProjectMembers
/projects/{projectId}:members:remove:
post:
operationId: removeProjectMembersWhy:A declarative-friendly resource exposes imperative add and remove methods, which express list changes as deltas a declarative client cannot reconcile against a desired state.
Identify the resources documented as declarative-friendly per IPA-127.
For each, list the operations that modify its repeated fields.
Confirm those fields are updated through the standard update method, and that no custom
AddorRemovemethods exist for them.Report any declarative-friendly resource that exposes
AddorRemovemethods for a repeated field.