Skip to content

Conversation

@sentik
Copy link
Contributor

@sentik sentik commented Oct 28, 2025

Fix: Handle allOf schemas with additional properties in zod generator

Problem

When a schema used allOf to extend a base type while also defining additional properties at the same level, the zod generator would only process the schemas in the allOf array and completely ignore the additional properties. This resulted in incomplete zod schemas that were missing critical fields.

Example

In an OpenAPI specification, a schema like SearchLeadPurposesListQueryResponseEntryPagingResult might have this structure:

{
  "title": "PagingResult<SearchLeadPurposesListQueryResponseEntry>",
  "required": ["items", "meta"],
  "type": "object",
  "allOf": [
    {
      "$ref": "#/components/schemas/PagingResult"
    }
  ],
  "properties": {
    "items": {
      "title": "IReadOnlyCollection<SearchLeadPurposesListQueryResponseEntry>",
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/SearchLeadPurposesListQueryResponseEntry"
      }
    }
  },
  "additionalProperties": false
}

Where PagingResult provides the meta property:

{
  "required": ["meta"],
  "type": "object",
  "properties": {
    "meta": {
      "$ref": "#/components/schemas/PagingResultMeta"
    }
  },
  "additionalProperties": false
}

Before the fix

The generated zod schema only included the base schema properties (meta) and completely ignored the items array:

export const searchLeadPurposesListResponse = zod.object({ 
  "entries": zod.object({ 
    "meta": zod.object({
      "total": zod.coerce.number(),
      "take": zod.coerce.number().min(...).max(...),
      "offset": zod.coerce.number().min(...).max(...)
    }) 
  }) 
})

After the fix

The generated zod schema now correctly includes both the base schema properties (meta) AND the additional properties (items array):

export const searchLeadPurposesListResponse = zod.object({
  "entries": zod.object({
    "meta": zod.object({
      "total": zod.number(),
      "take": zod.number().min(...).max(...),
      "offset": zod.number().min(...).max(...)
    })
  }).and(zod.object({
    "items": zod.array(zod.object({
      "leadPurposeId": zod.number().describe('...'),
      "name": zod.string().describe('...'),
      "code": zod.string().describe('...'),
      "description": zod.string().nullish().describe('...'),
      "isShared": zod.boolean().describe('...')
    }))
  }))
})

Technical Details

The fix was implemented in packages/zod/src/index.ts. When processing allOf schemas, the code now:

  1. Generates zod schema definitions for all schemas in the allOf array
  2. Checks if the original schema has additional properties defined at the same level
  3. If additional properties exist, generates a separate schema definition for them
  4. Adds the additional properties schema to the allOf array
  5. The resulting schemas are merged using zod's .and() method, which combines both schemas

This follows the OpenAPI specification where allOf combined with properties means "start with all properties from allOf schemas, then also add these properties".

Testing

  • Added comprehensive test case 'handles allOf with additional properties' in packages/zod/src/zod.test.ts
  • Test validates that the generated schema includes both base properties from allOf and additional properties
  • All existing tests pass (49 tests total in zod package)

Impact

This fix affects any OpenAPI schema that uses allOf with additional properties, ensuring complete zod schema generation. This is particularly important for paginated responses and other common API patterns where a base type is extended.

Files Changed

  • packages/zod/src/index.ts - Added logic to handle additional properties in allOf schemas
  • packages/zod/src/zod.test.ts - Added test case for allOf with additional properties

Fix #2340

Fix #2306

@melloware melloware added the zod Zod related issue label Oct 28, 2025
@melloware melloware added this to the 8.0.0-rc.0 milestone Oct 28, 2025
@melloware
Copy link
Collaborator

@sentik thank you. Can you check if your PR fixes either of these

#2340

#2306

@melloware
Copy link
Collaborator

looks like you need to run yarn format too

@melloware melloware merged commit f1f0b22 into orval-labs:master Oct 28, 2025
0 of 2 checks passed
zhu-hong pushed a commit to zhu-hong/orval that referenced this pull request Oct 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

zod Zod related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Zod: allOf doesn't work with type string Zod: required doesn't work with allOf

2 participants