diff --git a/fern/products/api-def/ferndef-pages/types.mdx b/fern/products/api-def/ferndef-pages/types.mdx index c9ef37bcb..fd268fdd6 100644 --- a/fern/products/api-def/ferndef-pages/types.mdx +++ b/fern/products/api-def/ferndef-pages/types.mdx @@ -22,10 +22,87 @@ Types describe the data model of your API. | `set` | An unordered collection with unique elements, e.g., `set` | | `map` | Key-value mapping, e.g., `map` | | `optional` | Optional value, e.g., `optional` | +| `nullable` | Nullable value that can be explicitly set to `null`, e.g., `nullable` | | `literal` | Literal value, e.g., `literal<"Plants">` | | `file` | File upload type, e.g., [file uploads](/learn/api-definition/fern/endpoints/multipart) | | `unknown` | Represents arbitrary JSON | +### Optional vs nullable + +Understanding the difference between `optional` and `nullable` is important for modeling your API: + +- **`optional`**: The field can be omitted from the request/response entirely. When omitted, the field isn't sent over the wire. +- **`nullable`**: The field must be present but can be explicitly set to `null`. This allows you to distinguish between "not specified" and "explicitly cleared". + +```yaml +types: + User: + properties: + name: string + nickname: optional # Can be omitted entirely + bio: nullable # Must be present, but can be null +``` + +This distinction is particularly important for HTTP PATCH requests where you need to differentiate between: +1. **Omitting a field** - Don't update this field (keep existing value) +2. **Setting to `null`** - Clear this field (remove the value) +3. **Setting to a value** - Update this field with a new value + +#### Example: HTTP PATCH request semantics + +```yaml +types: + UpdateUserRequest: + properties: + nickname: optional # Omit to keep current nickname + bio: nullable # Set to null to clear bio +``` + +**TypeScript SDK usage:** +```typescript +// Don't update nickname, clear bio +await client.users.update({ + bio: null // Explicitly clear the bio field + // nickname is omitted, so it won't be updated +}); + +// Update both fields +await client.users.update({ + nickname: "john_doe", + bio: "Software engineer" +}); +``` + +**Java SDK usage:** +```java +// With collapse-optional-nullable config enabled +UpdateUserRequest.builder() + .nickname(Optional.empty()) // Omit field (don't update) + .bio(OptionalNullable.ofNull()) // Clear field (set to null) + .build(); + +UpdateUserRequest.builder() + .nickname(Optional.of("john_doe")) + .bio(OptionalNullable.of("Software engineer")) + .build(); +``` + +### Combining optional and nullable + +You can combine `optional` and `nullable` to create a field that can be omitted OR explicitly set to null: + +```yaml +types: + User: + properties: + metadata: optional>> +``` + +This creates three possible states: +1. Field omitted entirely +2. Field present with `null` value +3. Field present with actual map data + ## Custom types Creating your own types is easy in Fern!