Skip to main content

OneOf and AnyOf

APIMatic's SDKs fully support the oneOf and anyOf types defined in OpenAPI specifications, allowing users to define flexible request parameters and response payloads that can accept or return different types of data. By leveraging these union types, users can represent alternative schemas or dynamic data structures in their APIs. This capability enhances the accuracy and reliability of API integrations, ensuring that SDKs can seamlessly handle complex API behaviors with improved validation and flexibility.

Configure OneOf and AnyOf in your OpenAPI Definition

Here is an example of OneOf and AnyOf types defined in OpenAPI definition:

paths:
/users/create:
post:
summary: Create a new user
operationId: createUser
requestBody:
required: true
content:
application/json:
schema:
anyOf:
- $ref: '#/components/schemas/Accountant'
- $ref: '#/components/schemas/Manager'
responses:
'201':
description: User created successfully
content:
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/AccountantResponse'
- $ref: '#/components/schemas/ManagerResponse'
...

The API call, shown in the OpenAPI format above, is a post-call that takes in either Accountant or Manager as request payload.

SDK Example

Here is how the parameter initialization would look like when calling the endpoint,

const body: CreateUserBody = {
accountId: 'A001',
firstName: 'John',
lastName: 'Doe',
};

async () => {
const { result, ...httpResponse } = await apiController.createUser(body);
};

In the code snippet above, the CreateUser method returns a response payload that can be either an AccountantResponse or a ManagerResponse instance. With the introduction of support for OneOf and AnyOf, the SDK explicitly validates that the response matches one of these specified types, ensuring more accurate and reliable response handling, as shown below:

async () => {
const { result, ...httpResponse } = await apiController.createUser(body);
if (CreateUserResponse.isAccountantResponse(result)) {
// Use the result narrowed down to AccountantResponse type.
} else if (CreateUserResponse.isManagerResponse(result)) {
// Use the result narrowed down to ManagerResponse type.
} else {
// Result is narrowed down to type 'never'.
}
};

Constraint-Aware anyOf Arms

An anyOf can list multiple schemas that share the same base type but differ by the constraints applied to them, for example a string restricted by a length range and a pattern alongside an email-formatted string. In these cases, the SDK represents each schema as its own distinct arm of the union, so every variant is preserved and available to work with individually.

When (de)serializing a value, the SDK selects the arm whose constraints the value satisfies. A value that meets the constraints of a more specific schema is routed to that arm, while any other value is routed to the remaining arm. This makes the handling of constrained union members precise and predictable.

responses:
'200':
description: OK
content:
application/json:
schema:
anyOf:
- type: string
minLength: 1
maxLength: 40
pattern: "^[a-z-]+$"
- type: string
format: email

For the definition above, the SDK generates a separate arm for each string schema. The endpoint returns the union as its response, and you read the value by checking each arm:

// The endpoint returns the union as its response.
var result = await client.GetContactAsync();

// Read the value by checking each arm.
if (result.TryGetEmailString(out var address))
{
Console.WriteLine($"Email: {address}");
}
else if (result.TryGetString(out var constrained))
{
Console.WriteLine($"Constrained string: {constrained}");
}

During deserialization the SDK routes the value automatically. A value such as "order-item" satisfies the length and pattern constraints and is deserialized into the constrained arm, while a value such as "user@example.com" is deserialized into the email-formatted arm.

note

Constraint-aware AnyOf arm selection is enforced by the SDK and is currently available in the C# v4 SDK (beta).

Validation Errors

Validation errors with helpful messages will be thrown whenever an input with an invalid type is provided to an endpoint or invalid typed data is received via a response from the server. This will enforce type strictness and validation of requests and responses within the SDK. These exceptions will be thrown for:

  • OneOf types: When either more than one acceptable type matches or no type matches at all against the provided value.

  • AnyOf types: When no acceptable type matches at all against the provided value.