IPA-125: Single Type in Request and Response
API requests and responses should strive for clarity by representing each field with a single, well-defined type. This helps maintain consistency and reduces ambiguity for API consumers. This convention also ensures that downstream tooling is well-supported and prevents issues for IaC tools as it simplifies state management.
Guidance
Splitting Fields for Multiple Value Types
API producers should not use
oneOfwith base types likeintegerorstringwhen the field can hold multiple distinct value types.components:
schemas:
Setting:
type: object
properties:
enabled:
type: boolean
threshold:
type: integerWhy:Each value type lives in its own typed field, so a consumer reads
enabledas a boolean andthresholdas an integer without inspecting the payload to find out which type arrived.components:
schemas:
Setting:
type: object
properties:
value:
oneOf:
- type: boolean
- type: integer
- type: stringWhy:A single field that may arrive as a boolean, an integer, or a string forces every consumer to branch on the runtime type, and generated clients cannot give the field one stable static type.
API producers should split such fields into separate, clearly named fields with appropriate types.
components:
schemas:
Index:
type: object
properties:
isArray:
type: boolean
arrayObjects:
type: array
items:
type: object
singleObject:
type: objectWhy:The three value shapes become three named fields with explicit types, so the name of each field documents what it holds and the type system enforces it.
components:
schemas:
Index:
type: object
properties:
index:
oneOf:
- type: boolean
- type: array
items:
type: object
- type: objectWhy:One overloaded field carries a boolean, an array, or an object depending on the case, so the field name describes none of them and a consumer cannot tell from the schema which shape to send.
For each schema under
$.components.schemas, list every property whose definition usesoneOf.For each such
oneOf, determine whether the members are distinct base types (boolean,integer,number,string) or different structural shapes (object versus array versus scalar).When the members are distinct value types rather than variants of one typed object, treat the single field as overloaded.
Report each overloaded field, noting that the fix is one named, single-typed field per value shape.
Depends on
Fields Containing Multiple Object Types
API producers may use fields that contain multiple objects when request and response objects allow explicitly setting the type of the object.
In OpenAPI each
oneOfproperty must be accompanied by adiscriminatorproperty that defines when each exact type is used.components:
schemas:
Notification:
oneOf:
- $ref: "#/components/schemas/EmailNotification"
- $ref: "#/components/schemas/SmsNotification"
discriminator:
propertyName: channel
mapping:
email: "#/components/schemas/EmailNotification"
sms: "#/components/schemas/SmsNotification"Why:The
discriminatornames the field (channel) that selects a variant and maps each value to one schema, so a consumer resolves the concrete type from the payload without guessing.components:
schemas:
Notification:
oneOf:
- $ref: "#/components/schemas/EmailNotification"
- $ref: "#/components/schemas/SmsNotification"Why:Without a
discriminator, nothing in the schema indicates which variant a given payload represents, so the concrete type must be inferred by trial validation against each branch.In OpenAPI each
discriminatorproperty must be accompanied by aoneOf,anyOf, orallOfproperty (OAS 3.1.0 4.8.25.1).components:
schemas:
Payment:
oneOf:
- $ref: "#/components/schemas/CardPayment"
- $ref: "#/components/schemas/BankPayment"
discriminator:
propertyName: methodWhy:The
discriminatorsits beside aoneOfcomposition, so it has a set of candidate schemas to select among.components:
schemas:
Payment:
type: object
properties:
method:
type: string
discriminator:
propertyName: methodWhy:A
discriminatorwith nooneOf,anyOf, orallOfsibling has no candidate schemas to choose between, so the selection it describes points at nothing.