-
-
Notifications
You must be signed in to change notification settings - Fork 254
Description
Description
The Zod plugin generates invalid TypeScript code when dealing with OpenAPI schemas that contain circular references between entities. This results in compilation errors due to variables being used before their declaration.
Bug Report
Current Behavior
When generating Zod schemas from an OpenAPI spec with circular references, the generated code contains TypeScript errors:
Block-scoped variable 'zSubregionDto' used before its declaration.
Expected Behavior
The generated Zod schemas should handle circular references properly, either by:
- Using
z.lazy()to wrap circular references - Reordering declarations to avoid forward references
- Providing configuration options to handle circular dependencies
Minimal Reproduction
OpenAPI Schema (simplified):
components:
schemas:
CountryDto:
type: object
properties:
id:
type: number
name:
type: string
region:
$ref: '#/components/schemas/RegionDto'
subregion:
$ref: '#/components/schemas/SubregionDto'
SubregionDto:
type: object
properties:
id:
type: number
name:
type: string
region:
$ref: '#/components/schemas/RegionDto'
countries:
type: array
items:
$ref: '#/components/schemas/CountryDto'
RegionDto:
type: object
properties:
id:
type: number
name:
type: string
countries:
type: array
items:
$ref: '#/components/schemas/CountryDto'
subregions:
type: array
items:
$ref: '#/components/schemas/SubregionDto'Generated Code (problematic):
export const zCountryDto = z.object({
// ... other properties
get region() {
return zRegionDto; // ✅ This works with getter
},
subregion: zSubregionDto, // ❌ Error: used before declaration
// ...
});
export const zSubregionDto = z.object({
// ... defined later
});Configuration:
// openapi-ts.config.ts
export default defineConfig({
input: "http://localhost:3000/api-yaml",
output: {
path: "src/client",
format: "prettier",
lint: "eslint",
tsConfigPath: "./tsconfig.json",
},
plugins: [
"@hey-api/schemas",
{ name: "@hey-api/client-axios" },
"@tanstack/react-query",
"zod", // ❌ No options available for circular reference handling
{
dates: true,
name: "@hey-api/transformers",
},
{
enums: "javascript",
name: "@hey-api/typescript",
},
],
});System Information
- @hey-api/openapi-ts version: 0.84.0
- Node.js version: v24.8.0
- npm version: 11.6.0
- TypeScript version: 5.9.2
- Operating System: macOS (darwin 25.0.0)
- Package manager: pnpm
Dependencies
{
"devDependencies": {
"@hey-api/openapi-ts": "0.84.0"
},
"dependencies": {
"@hey-api/client-axios": "0.9.1",
"zod": "^3.x"
}
}Attempted Solutions
-
Tried configuration options (not available):
// ❌ These don't exist in current version "zod": { circularReferenceHandler: "lazy" }
-
Attempted workarounds:
- Removing circular references from API schema (not always feasible)
- Manual post-processing of generated files (not sustainable)
Proposed Solutions
-
Add
z.lazy()wrapper for circular references:export const zCountryDto = z.object({ subregion: z.lazy(() => zSubregionDto), // ✅ Wrapped in lazy });
-
Add configuration option:
plugins: [ { name: "zod", circularReferenceHandler: "lazy" | "reorder" | "error" } ]
-
Automatic dependency sorting to reorder declarations properly
Related Issues
- Similar issue reported: [Zod Plugin] Variable used before declaration in OpenAPI schema with recursive and self-referential types #2560
- Related to circular dependency handling in Zod schema generation
Additional Context
This issue commonly occurs with:
- Geographic entities (Country → Region → Country)
- User/Organization relationships
- Category/Subcategory hierarchies
- Any bidirectional entity relationships
The issue prevents using the Zod plugin with realistic API schemas that model real-world entity relationships.
Reproducible example or configuration
https://stackblitz.com/edit/hey-api-client-fetch-example
OpenAPI specification (optional)
No response
System information (optional)
No response