Skip to content

feat(openapi): gen spec with common schemas - components.schemas#638

Merged
dinwwwh merged 9 commits intomainfrom
feat/openapi/components-schema
Jun 13, 2025
Merged

feat(openapi): gen spec with common schemas - components.schemas#638
dinwwwh merged 9 commits intomainfrom
feat/openapi/components-schema

Conversation

@dinwwwh
Copy link
Copy Markdown
Member

@dinwwwh dinwwwh commented Jun 12, 2025

Common Schemas

Define reusable schema components that can be referenced across your OpenAPI specification:

const UserSchema = z.object({
  id: z.string(),
  name: z.string(),
  email: z.string().email(),
})

const PetSchema = z.object({
  id: z.string().transform(id => Number(id)).pipe(z.number()),
})

const spec = await generator.generate(router, {
  commonSchemas: {
    User: {
      schema: UserSchema,
    },
    InputPet: {
      strategy: 'input',
      schema: PetSchema,
    },
    OutputPet: {
      strategy: 'output',
      schema: PetSchema,
    },
  },
})

The strategy option determines which schema definition to use when input and output types differ (defaults to input). This is needed because we cannot use the same $ref for both input and output in this case.

Summary by CodeRabbit

  • New Features

    • Added support for defining and reusing common schemas in OpenAPI specifications, enabling shared schema components to be referenced throughout API documentation.
    • Introduced options to control input/output schema strategies and recursive schema handling for improved API schema consistency.
    • Added maximum structure depth control to schema converters to prevent excessive recursion and improve performance.
  • Documentation

    • Updated OpenAPI documentation to explain the use of common schemas and provide usage examples.
  • Tests

    • Added comprehensive tests to validate common schema referencing, recursive schema handling, and component reuse in OpenAPI and Zod schema converters.
    • Added tests for resolving JSON Schema $ref references within OpenAPI documents.
  • Chores

    • Integrated common schema registration in multiple API playgrounds, enhancing API documentation with reusable schema definitions.

## Common Schemas

Define reusable schema components that can be referenced across your OpenAPI specification:

```ts
const UserSchema = z.object({
  id: z.string(),
  name: z.string(),
  email: z.string().email(),
})

const PetSchema = z.object({
  id: z.string().transform(id => Number(id)).pipe(z.number()),
})

const spec = await generator.generate(router, {
  commonSchemas: {
    User: {
      schema: UserSchema,
    },
    InputPet: {
      strategy: 'input',
      schema: PetSchema,
    },
    OutputPet: {
      strategy: 'output',
      schema: PetSchema,
    },
  },
})
```

:::info
The `strategy` option determines which schema definition to use when input and output types differ (defaults to `input`). This is needed because we cannot use the same `$ref` for both input and output in this case.
:::
@vercel
Copy link
Copy Markdown

vercel Bot commented Jun 12, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
orpc ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jun 13, 2025 1:25am

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 12, 2025

Walkthrough

This update introduces support for reusable "common schemas" with explicit input/output strategies in the OpenAPI generator and Zod schema converters. Documentation and integration tests are added, and playgrounds are updated to register shared schemas. The Zod and Zod v4 converters gain options for reference depth and structure limits.

Changes

File(s) Change Summary
apps/content/docs/openapi/openapi-specification.md Added documentation for "Common Schemas" and usage examples with strategies.
packages/openapi/src/openapi-generator.ts
packages/openapi/src/schema-converter.ts
Added support for commonSchemas option, schema component resolution, and reference strategy handling.
packages/openapi/src/openapi-generator.test.ts Added integration tests for OpenAPI generator with common schemas and recursive/detailed input/output structures.
packages/openapi/src/openapi-utils.ts
packages/openapi/src/openapi-utils.test.ts
Added resolveOpenAPIJsonSchemaRef function and tests to resolve JSON Schema $ref in OpenAPI documents.
packages/zod/src/converter.ts
packages/zod/src/zod4/converter.ts
Added maxStructureDepth and minStructureDepthForRef options; updated recursive conversion logic for references.
packages/zod/src/converter.components.test.ts
packages/zod/src/zod4/converter.components.test.ts
Added unit tests for Zod and Zod v4 converters, focusing on component referencing, recursion, and strategy handling.
packages/zod/src/zod4/converter.test.ts Added test for recursive schema conversion in Zod v4 converter.
packages/zod/src/converter.test.ts Added test for recursive schema conversion in Zod converter.
Playground API route files (playgrounds/...) Registered user, auth, and planet schemas as commonSchemas in OpenAPI handler/generator configuration.
playgrounds/astro/src/pages/api/[...rest].ts
playgrounds/contract-first/src/main.ts
playgrounds/nest/src/reference/reference.service.ts
playgrounds/next/src/app/api/[[...rest]]/route.ts
playgrounds/nuxt/server/routes/api/[...].ts
playgrounds/solid-start/src/routes/api/[...rest].ts
playgrounds/svelte-kit/src/routes/api/[...rest]/+server.ts
playgrounds/tanstack-start/src/routes/api/$.ts
Imported and registered shared Zod schemas as reusable OpenAPI components.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant OpenAPIGenerator
    participant SchemaConverter
    participant OpenAPIDoc

    User->>OpenAPIGenerator: generate({ commonSchemas })
    OpenAPIGenerator->>OpenAPIGenerator: #resolveCommonSchemas(commonSchemas)
    OpenAPIGenerator->>SchemaConverter: convert(schema, { strategy, ... })
    SchemaConverter-->>OpenAPIGenerator: JSON Schema component(s)
    OpenAPIGenerator->>OpenAPIDoc: Add components.schemas
    OpenAPIGenerator->>OpenAPIDoc: Generate paths using $ref to commonSchemas
    OpenAPIGenerator-->>User: OpenAPI spec with reusable components
Loading

Possibly related PRs

  • unnoq/orpc#462: Introduced experimental Zod v4 support, which this PR extends with common schema and strategy features.
  • unnoq/orpc#208: Rewrote the OpenAPIGenerator for modularization and contract-based types; this PR builds on that foundation.

Poem

In the warren of schemas, we hop with delight,
Now sharing our forms—input, output in sight!
With strategies chosen, and references neat,
Our OpenAPI docs are robust and complete.
🐇✨ Hooray for common schemas—oh, what a treat!

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary of Changes

Hello @unnoq, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request adds support for defining and referencing common schemas in the OpenAPI specification generated from your router. This allows for better organization and reusability of schema definitions within the spec, particularly useful for complex or recursive types. It includes options to handle schemas with different input/output transformations and control when references are used based on nesting depth.

Highlights

  • Schema Referencing ($ref): The generator now uses $ref pointers to reference the defined common schemas within the generated OpenAPI paths, reducing duplication and improving readability of the spec.
  • Input/Output Strategy: Adds a strategy option for common schemas to specify whether the input or output transformation of a Zod schema should be used for the component definition, addressing cases where schemas transform data differently.
  • Depth Control for References: Adds a minStructureDepthForRef option to the schema converter to control the minimum nesting depth required for a schema to be replaced by a $ref to a common component.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments to provide feedback.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configureGemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a significant enhancement by adding support for components.schemas in the OpenAPI generator. This allows for defining reusable schemas, improving the modularity and readability of the generated OpenAPI specifications. Key changes include:

  • A new commonSchemas option in OpenAPIGeneratorGenerateOptions to define these reusable components.
  • A strategy option within commonSchemas to handle schemas that have different input and output representations (e.g., due to Zod transforms).
  • Updates to the Zod schema converters (for both Zod v3 and Zod v4) to recognize and use these common components via $ref, respecting a new minStructureDepthForRef option to control inlining vs. referencing.
  • Introduction of a maxStructureDepth option in Zod converters as a safeguard against excessive recursion during schema conversion.

The implementation appears robust, with careful consideration for how schemas are resolved and referenced. The tests are comprehensive, covering various scenarios including recursive schemas, different strategies, and varying structure depths for references. The documentation has also been updated clearly.

The feedback provided focuses on minor clarity improvements for complex conditional logic and a note on the schema comparison technique, aiming for enhanced long-term maintainability. Overall, this is a well-executed feature that will be very beneficial.

Comment thread packages/openapi/src/openapi-generator.ts
Comment thread packages/openapi/src/openapi-generator.ts
Comment thread packages/zod/src/converter.ts
Comment thread packages/zod/src/zod4/converter.ts
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Jun 12, 2025

More templates

@orpc/arktype

npm i https://pkg.pr.new/@orpc/arktype@638

@orpc/client

npm i https://pkg.pr.new/@orpc/client@638

@orpc/contract

npm i https://pkg.pr.new/@orpc/contract@638

@orpc/hey-api

npm i https://pkg.pr.new/@orpc/hey-api@638

@orpc/nest

npm i https://pkg.pr.new/@orpc/nest@638

@orpc/openapi

npm i https://pkg.pr.new/@orpc/openapi@638

@orpc/openapi-client

npm i https://pkg.pr.new/@orpc/openapi-client@638

@orpc/react

npm i https://pkg.pr.new/@orpc/react@638

@orpc/react-query

npm i https://pkg.pr.new/@orpc/react-query@638

@orpc/server

npm i https://pkg.pr.new/@orpc/server@638

@orpc/shared

npm i https://pkg.pr.new/@orpc/shared@638

@orpc/solid-query

npm i https://pkg.pr.new/@orpc/solid-query@638

@orpc/standard-server

npm i https://pkg.pr.new/@orpc/standard-server@638

@orpc/standard-server-aws-lambda

npm i https://pkg.pr.new/@orpc/standard-server-aws-lambda@638

@orpc/standard-server-fetch

npm i https://pkg.pr.new/@orpc/standard-server-fetch@638

@orpc/standard-server-node

npm i https://pkg.pr.new/@orpc/standard-server-node@638

@orpc/standard-server-peer

npm i https://pkg.pr.new/@orpc/standard-server-peer@638

@orpc/svelte-query

npm i https://pkg.pr.new/@orpc/svelte-query@638

@orpc/tanstack-query

npm i https://pkg.pr.new/@orpc/tanstack-query@638

@orpc/valibot

npm i https://pkg.pr.new/@orpc/valibot@638

@orpc/vue-colada

npm i https://pkg.pr.new/@orpc/vue-colada@638

@orpc/vue-query

npm i https://pkg.pr.new/@orpc/vue-query@638

@orpc/zod

npm i https://pkg.pr.new/@orpc/zod@638

commit: b65f6de

@dinwwwh dinwwwh changed the title feat(openapi): support components.schema feat(openapi): gen spec with common schemas (components.schema) Jun 12, 2025
@dinwwwh dinwwwh changed the title feat(openapi): gen spec with common schemas (components.schema) feat(openapi): gen spec with common schemas (components.schemas) Jun 12, 2025
@dinwwwh dinwwwh changed the title feat(openapi): gen spec with common schemas (components.schemas) feat(openapi): gen spec with common schemas - components.schemas Jun 12, 2025
@dinwwwh dinwwwh marked this pull request as ready for review June 12, 2025 13:11
@dosubot dosubot Bot added the size:XXL This PR changes 1000+ lines, ignoring generated files. label Jun 12, 2025
@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 12, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (4)
playgrounds/next/src/app/api/[[...rest]]/route.ts (1)

28-36: Same commonSchemas issues as in the Svelte-Kit playground

The mixed naming (TokenSchema) and missing strategy flags recur here. Apply the same normalisation/refactor as suggested for +server.ts to keep all playgrounds aligned.

playgrounds/nest/src/reference/reference.service.ts (1)

30-38: Replicated naming / strategy mismatch

The Nest playground copies the same commonSchemas block; please propagate the earlier fixes here as well to avoid divergent component IDs across specs generated by different demos.

packages/openapi/src/openapi-generator.ts (1)

171-174: JSON.stringify equality caveat (same as prior feedback)

Using stringifyJSON to decide if input/output variants are identical is pragmatic but relies on key order stability.
If generation logic changes, semantically-equivalent schemas with different key order will be treated as different.

(See previous review on identical pattern.)

packages/openapi/src/openapi-generator.test.ts (1)

978-993: Same self-reference issue for User – prefer z.lazy

The pattern get parent() { return User.optional() } recreates a schema each access and may cause converter cache misses.
Apply the z.lazy pattern here as well to keep object identity stable and reduce allocations.

🧹 Nitpick comments (17)
playgrounds/contract-first/src/main.ts (1)

30-38: Consistent schema key naming in commonSchemas

The key TokenSchema includes the Schema suffix while other keys (e.g., Credential) do not. Consider using a uniform naming convention for component keys, for example renaming to Token:

- TokenSchema: { schema: TokenSchema },
+ Token: { schema: TokenSchema },
playgrounds/nuxt/server/routes/api/[...].ts (1)

27-35: Consistent schema key naming in commonSchemas

Within commonSchemas, the key TokenSchema diverges from the pattern used by other entries. To maintain uniform component naming:

- TokenSchema: { schema: TokenSchema },
+ Token: { schema: TokenSchema },
playgrounds/tanstack-start/src/routes/api/$.ts (1)

30-38: Consistent schema key naming in commonSchemas

The TokenSchema key adds an extra suffix compared to other schema keys. Standardize component names by renaming it:

- TokenSchema: { schema: TokenSchema },
+ Token: { schema: TokenSchema },
playgrounds/solid-start/src/routes/api/[...rest].ts (1)

29-37: Consistent schema key naming in commonSchemas

The commonSchemas map uses TokenSchema as a key, which is inconsistent with other entries (e.g., Credential). Rename for consistency:

- TokenSchema: { schema: TokenSchema },
+ Token: { schema: TokenSchema },
playgrounds/astro/src/pages/api/[...rest].ts (1)

29-37: Consistent schema key naming in commonSchemas

The mapping uses TokenSchema while other entries omit the suffix. To keep component names consistent, rename to Token:

- TokenSchema: { schema: TokenSchema },
+ Token: { schema: TokenSchema },
packages/zod/src/zod4/converter.test.ts (1)

10-33: Prefer z.lazy for recursion – getter hack is brittle

Using a getter that references Schema inside its own definition relies on the variable being initialised later and is easy to break with refactors. z.lazy is the idiomatic, safe way:

-const Schema = z.object({
-  id: z.string(),
-  name: z.string(),
-  get parents() {
-    return z.array(Schema).optional()
-  },
-})
+const Schema: z.ZodType<any> = z.lazy(() =>
+  z.object({
+    id: z.string(),
+    name: z.string(),
+    parents: z.array(Schema).optional(),
+  }),
+)

The test expectations remain unchanged, but this avoids TDZ hazards and improves readability.

apps/content/docs/openapi/openapi-specification.md (1)

126-139: Variable name in snippet doesn’t exist

Earlier in the doc the generator is instantiated as openAPIGenerator, but the snippet uses generator.generate(...), which will confuse readers copying the code.

-const spec = await generator.generate(router, {
+const spec = await openAPIGenerator.generate(router, {

Additionally, align naming with the playground fixes (Token vs TokenSchema) to stay consistent across docs and code.

packages/zod/src/converter.ts (2)

78-85: Drop the redundant default assignment on maxStructureDepth.

maxStructureDepth is initialised to 100 and immediately overwritten in the constructor (options.maxStructureDepth ?? 10).
Keeping both values adds noise and can mislead readers about the real default.

-private readonly maxStructureDepth: Exclude<ZodToJsonSchemaOptions['maxStructureDepth'], undefined> = 100
+private readonly maxStructureDepth: Exclude<ZodToJsonSchemaOptions['maxStructureDepth'], undefined>

107-115: Avoid O(N) component look-ups on every recursion.

toArray(options.components) is walked for every nested property; on large, deeply-nested schemas this becomes a hot path.

Consider pre-processing components once (e.g. new Map(schema → component)) outside the recursion to achieve O(1) look-ups.

packages/zod/src/converter.components.test.ts (1)

8-10: Avoid any in test fixtures where possible.

Replacing the return type with z.ZodOptional<...> (or ReturnType<typeof z.array> etc.) preserves type-safety without affecting test readability.

packages/zod/src/zod4/converter.ts (2)

88-90: Misleading default value for maxStructureDepth

The field is initialised to 100 but overwritten in the constructor with options.maxStructureDepth ?? 10.
Drop the redundant initializer to avoid confusion.

-  private readonly maxStructureDepth: Exclude<experimental_ZodToJsonSchemaOptions['maxStructureDepth'], undefined> = 100
+  private readonly maxStructureDepth: Exclude<experimental_ZodToJsonSchemaOptions['maxStructureDepth'], undefined>

130-138: Component lookup can become O(N²)

toArray(options.components) allocates a new array on every recursive call and linear-scans it.
For deeply-nested schemas with many components this degrades performance.

Consider:

  • caching components outside recursion
  • converting the array to a Map<AnySchema, SchemaConverterComponent> for O(1) look-ups.
packages/openapi/src/openapi-generator.ts (2)

161-189: Duplicate component entries risk

for … in commonSchemas blindly pushes into baseOptions.components.
If a user supplies two aliases for the same schema the converter will store two identical components and $ref resolution may become ambiguous.

Deduplicate by schema (reference equality) or by key before pushing.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 179-180: packages/openapi/src/openapi-generator.ts#L179-L180
Added lines #L179 - L180 were not covered by tests


194-205: Potential quadratic convert calls

Each common schema is converted once per strategy inside the outer loop, then converted again when materialising doc.components.schemas.
For large sets this doubles work; caching the earlier json would avoid re-conversion.

packages/openapi/src/schema-converter.ts (1)

5-10: Mark component fields immutable & clarify intent

SchemaConverterComponent instances are treated as value-objects once registered.
Making their fields readonly prevents accidental mutation (e.g. .ref rewrites during conversion) and immediately signals immutability to readers.

-export interface SchemaConverterComponent {
-  allowedStrategies: SchemaConvertOptions['strategy'][]
-  schema: AnySchema
-  required: boolean
-  ref: string
-}
+export interface SchemaConverterComponent {
+  readonly allowedStrategies: SchemaConvertOptions['strategy'][]
+  readonly schema: AnySchema
+  /** Whether the component itself is required when referenced */
+  readonly required: boolean
+  /** Fully-qualified `$ref` pointing into `#/components/schemas/...` */
+  readonly ref: string
+}
packages/zod/src/zod4/converter.components.test.ts (1)

192-210: Shadowing Pet obscures earlier constant

Re-declaring const Pet inside the "unsupported strategy" test block hides the outer-scope Pet, which may confuse maintainers skimming the file.

Rename the inner constant or inline the schema:

-const Pet = z.object({
+const UnsupportedPet = z.object({
   id: z.string(),
   name: z.string(),
 })
packages/openapi/src/openapi-generator.test.ts (1)

978-1393: Monster assertion block – extract helpers for readability

The single "generator - commonSchemas" test spans ~400 lines of deeply nested expectations, which is hard to maintain. Consider:

  1. Splitting the test into smaller it blocks (e.g. “components are emitted”, “/user route”, “/pet route”…).
  2. Extracting expectation builders:
function expectRef(schemaName: string) {
  return { $ref: `#/components/schemas/${schemaName}` }
}
  1. Using expect.objectContaining + helpers to reduce duplication.

This refactor will make failures more actionable and future contributors less hesitant to touch the spec.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5b2d161 and 8aadc15.

📒 Files selected for processing (17)
  • apps/content/docs/openapi/openapi-specification.md (1 hunks)
  • packages/openapi/src/openapi-generator.test.ts (2 hunks)
  • packages/openapi/src/openapi-generator.ts (10 hunks)
  • packages/openapi/src/schema-converter.ts (1 hunks)
  • packages/zod/src/converter.components.test.ts (1 hunks)
  • packages/zod/src/converter.ts (17 hunks)
  • packages/zod/src/zod4/converter.components.test.ts (1 hunks)
  • packages/zod/src/zod4/converter.test.ts (1 hunks)
  • packages/zod/src/zod4/converter.ts (19 hunks)
  • playgrounds/astro/src/pages/api/[...rest].ts (2 hunks)
  • playgrounds/contract-first/src/main.ts (2 hunks)
  • playgrounds/nest/src/reference/reference.service.ts (2 hunks)
  • playgrounds/next/src/app/api/[[...rest]]/route.ts (2 hunks)
  • playgrounds/nuxt/server/routes/api/[...].ts (2 hunks)
  • playgrounds/solid-start/src/routes/api/[...rest].ts (2 hunks)
  • playgrounds/svelte-kit/src/routes/api/[...rest]/+server.ts (2 hunks)
  • playgrounds/tanstack-start/src/routes/api/$.ts (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (11)
playgrounds/tanstack-start/src/routes/api/$.ts (3)
playgrounds/tanstack-start/src/schemas/user.ts (2)
  • NewUserSchema (7-22)
  • UserSchema (24-39)
playgrounds/tanstack-start/src/schemas/auth.ts (2)
  • CredentialSchema (3-6)
  • TokenSchema (8-10)
playgrounds/tanstack-start/src/schemas/planet.ts (3)
  • NewPlanetSchema (9-13)
  • UpdatePlanetSchema (15-20)
  • PlanetSchema (22-28)
playgrounds/astro/src/pages/api/[...rest].ts (3)
playgrounds/astro/src/schemas/user.ts (2)
  • NewUserSchema (7-22)
  • UserSchema (24-39)
playgrounds/astro/src/schemas/auth.ts (2)
  • CredentialSchema (3-6)
  • TokenSchema (8-10)
playgrounds/astro/src/schemas/planet.ts (3)
  • NewPlanetSchema (9-13)
  • UpdatePlanetSchema (15-20)
  • PlanetSchema (22-28)
playgrounds/solid-start/src/routes/api/[...rest].ts (3)
playgrounds/solid-start/src/schemas/user.ts (2)
  • NewUserSchema (7-22)
  • UserSchema (24-39)
playgrounds/solid-start/src/schemas/auth.ts (2)
  • CredentialSchema (3-6)
  • TokenSchema (8-10)
playgrounds/solid-start/src/schemas/planet.ts (3)
  • NewPlanetSchema (9-13)
  • UpdatePlanetSchema (15-20)
  • PlanetSchema (22-28)
playgrounds/svelte-kit/src/routes/api/[...rest]/+server.ts (3)
playgrounds/svelte-kit/src/schemas/user.ts (2)
  • NewUserSchema (7-22)
  • UserSchema (24-39)
playgrounds/svelte-kit/src/schemas/auth.ts (2)
  • CredentialSchema (3-6)
  • TokenSchema (8-10)
playgrounds/svelte-kit/src/schemas/planet.ts (3)
  • NewPlanetSchema (9-13)
  • UpdatePlanetSchema (15-20)
  • PlanetSchema (22-28)
playgrounds/nest/src/reference/reference.service.ts (3)
playgrounds/nest/src/schemas/user.ts (2)
  • NewUserSchema (7-22)
  • UserSchema (24-39)
playgrounds/nest/src/schemas/auth.ts (2)
  • CredentialSchema (3-6)
  • TokenSchema (8-10)
playgrounds/nest/src/schemas/planet.ts (3)
  • NewPlanetSchema (9-13)
  • UpdatePlanetSchema (15-20)
  • PlanetSchema (22-28)
packages/zod/src/zod4/converter.test.ts (2)
packages/zod/src/zod4/converter.ts (1)
  • required (577-592)
packages/zod/src/converter.ts (1)
  • required (663-671)
packages/zod/src/converter.components.test.ts (2)
packages/zod/src/converter.ts (2)
  • ZodToJsonSchemaConverter (76-672)
  • required (663-671)
packages/zod/src/zod4/converter.ts (1)
  • required (577-592)
packages/openapi/src/schema-converter.ts (1)
packages/contract/src/schema.ts (1)
  • AnySchema (7-7)
packages/zod/src/converter.ts (4)
packages/openapi/src/schema.ts (1)
  • JSONSchema (6-6)
packages/openapi/src/schema-converter.ts (1)
  • ConditionalSchemaConverter (34-36)
packages/zod/src/zod4/converter.ts (3)
  • required (577-592)
  • schema (115-552)
  • schema (554-575)
packages/shared/src/array.ts (1)
  • toArray (1-3)
packages/zod/src/zod4/converter.components.test.ts (1)
packages/zod/src/zod4/converter.ts (1)
  • required (577-592)
playgrounds/next/src/app/api/[[...rest]]/route.ts (3)
playgrounds/next/src/schemas/user.ts (2)
  • NewUserSchema (7-22)
  • UserSchema (24-39)
playgrounds/next/src/schemas/auth.ts (2)
  • CredentialSchema (3-6)
  • TokenSchema (8-10)
playgrounds/next/src/schemas/planet.ts (3)
  • NewPlanetSchema (9-13)
  • UpdatePlanetSchema (15-20)
  • PlanetSchema (22-28)
🪛 GitHub Check: codecov/patch
packages/zod/src/converter.ts

[warning] 104-105: packages/zod/src/converter.ts#L104-L105
Added lines #L104 - L105 were not covered by tests

packages/openapi/src/openapi-generator.ts

[warning] 179-180: packages/openapi/src/openapi-generator.ts#L179-L180
Added lines #L179 - L180 were not covered by tests

🔇 Additional comments (6)
playgrounds/contract-first/src/main.ts (1)

9-11: Imports common schemas for OpenAPI spec

The new imports correctly bring in the shared Zod schemas for users, auth, and planets, which will be registered as reusable components.

playgrounds/nuxt/server/routes/api/[...].ts (1)

6-8: Imports common schemas in Nuxt API route

The updated imports correctly load the shared Zod schemas from the server directory for inclusion in the OpenAPI spec.

playgrounds/tanstack-start/src/routes/api/$.ts (1)

9-11: Imports shared schemas for TanStack API route

These imports correctly reference the common Zod schemas for User, Auth, and Planet, enabling their reuse across the OpenAPI spec.

playgrounds/solid-start/src/routes/api/[...rest].ts (1)

8-10: Import reusable schemas for Solid Start

The new imports correctly include the common Zod schemas, ensuring they're available for component registration in the OpenAPI spec.

playgrounds/astro/src/pages/api/[...rest].ts (1)

8-10: Import common Zod schemas in Astro route

The added imports correctly fetch the shared schemas from the Astro project, ready for OpenAPI component registration.

packages/zod/src/converter.ts (1)

103-105: No tests exercise the max-depth fallback path.

The branch returning anyJsonSchema when structureDepth > this.maxStructureDepth is currently uncovered.
Please add a unit test that creates a schema nested beyond the limit to keep this safeguard from regressing.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 104-105: packages/zod/src/converter.ts#L104-L105
Added lines #L104 - L105 were not covered by tests

Comment thread playgrounds/svelte-kit/src/routes/api/[...rest]/+server.ts
Comment thread packages/zod/src/zod4/converter.ts
Comment thread packages/openapi/src/schema-converter.ts
Comment thread packages/zod/src/zod4/converter.components.test.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
packages/zod/src/converter.test.ts (2)

630-637: Guard against excessive recursion depth

createSchema(100) builds a 100-level nested object through synchronous recursion.
While fine in CI, increasing depth (or user-supplied input) could exceed the JS call-stack.
Consider:

  • Using z.lazy with maxLazyDepth to cap traversal.
  • Or refactoring createSchema to an iterative build if deeper trees might be tested later.

Not blocking, but worth keeping in mind for long-running fuzz tests.


641-655: Add stronger assertions on nested “children” objects

Currently the expectation only checks for items: { type: 'object' }, which would pass even if
required keys (id, name) were omitted in deeper levels.

-      children: {
-        type: 'array',
-        items: expect.objectContaining({ type: 'object' }),
-      },
+      children: {
+        type: 'array',
+        items: expect.objectContaining({
+          type: 'object',
+          required: ['id', 'name'],
+        }),
+      },

This tiny addition fortifies the test against regressions that strip the inner-object structure while keeping the outer signature intact.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8aadc15 and fbb5840.

📒 Files selected for processing (12)
  • packages/openapi/src/openapi-generator.test.ts (2 hunks)
  • packages/zod/src/converter.test.ts (1 hunks)
  • packages/zod/src/converter.ts (16 hunks)
  • packages/zod/src/zod4/converter.ts (18 hunks)
  • playgrounds/astro/src/pages/api/[...rest].ts (2 hunks)
  • playgrounds/contract-first/src/main.ts (2 hunks)
  • playgrounds/nest/src/reference/reference.service.ts (2 hunks)
  • playgrounds/next/src/app/api/[[...rest]]/route.ts (2 hunks)
  • playgrounds/nuxt/server/routes/api/[...].ts (2 hunks)
  • playgrounds/solid-start/src/routes/api/[...rest].ts (2 hunks)
  • playgrounds/svelte-kit/src/routes/api/[...rest]/+server.ts (2 hunks)
  • playgrounds/tanstack-start/src/routes/api/$.ts (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • playgrounds/astro/src/pages/api/[...rest].ts
🚧 Files skipped from review as they are similar to previous changes (10)
  • playgrounds/contract-first/src/main.ts
  • playgrounds/tanstack-start/src/routes/api/$.ts
  • playgrounds/next/src/app/api/[[...rest]]/route.ts
  • playgrounds/svelte-kit/src/routes/api/[...rest]/+server.ts
  • playgrounds/solid-start/src/routes/api/[...rest].ts
  • playgrounds/nuxt/server/routes/api/[...].ts
  • playgrounds/nest/src/reference/reference.service.ts
  • packages/openapi/src/openapi-generator.test.ts
  • packages/zod/src/converter.ts
  • packages/zod/src/zod4/converter.ts
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: lint
  • GitHub Check: publish-commit

Comment thread packages/zod/src/converter.test.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
packages/openapi/src/openapi-generator.ts (1)

171-179: stringifyJSON equality caveat already noted

The equality check that relies on stringifyJSON suffers from key-ordering issues (see earlier review on lines 171/178 of this file).
If the order changes the comparison returns false even though the schemas are semantically identical.
A deep structural comparison would be safer.

🧹 Nitpick comments (4)
packages/openapi/src/openapi-utils.ts (1)

168-179: Consider supporting JSON-Pointer escaping to avoid ambiguous keys

resolveOpenAPIJsonSchemaRef extracts the slice after #/components/schemas/ verbatim ('c/c' stays 'c/c').
According to the JSON-Pointer spec, slashes within a key must be escaped as ~1; consumers that rely on strict pointers will therefore fail to dereference #/components/schemas/c/c.

You could transparently unescape ~1/ (and ~0~) when looking up the component, and, more importantly, escape component names when you generate $refs.

-const name = schema.$ref.slice(OPENAPI_JSON_SCHEMA_REF_PREFIX.length)
+const raw = schema.$ref.slice(OPENAPI_JSON_SCHEMA_REF_PREFIX.length)
+const name = raw.replace(/~1/g, '/').replace(/~0/g, '~')

Doing so keeps you RFC-6901-compliant and prevents subtle interop issues.

packages/openapi/src/openapi-utils.test.ts (2)

333-348: Tests rely on non-spec $ref – encode / with ~1

The case that checks #/components/schemas/c/c passes only because the implementation treats the whole tail as a property name.
Strict JSON-Pointer rules require the slash to be escaped (#/components/schemas/c~1c). Otherwise a compliant tool would look for components.schemas.c.c.

Updating the fixture keeps the test future-proof once pointer-escaping is supported:

-        'c/c': { type: 'object' },
+        'c/c': { type: 'object' },
...
-    expect(resolveOpenAPIJsonSchemaRef(doc, { $ref: '#/components/schemas/c/c' })).toEqual({ type: 'object' })
+    expect(resolveOpenAPIJsonSchemaRef(doc, { $ref: '#/components/schemas/c~1c' })).toEqual({ type: 'object' })

357-365: Minor phrasing / grammar

it('it do nothing if have no components.schemas' …) reads awkwardly.
Consider it('does nothing when components.schemas is missing', …) for clarity.

packages/openapi/src/openapi-generator.ts (1)

88-90: Prefer deleting commonSchemas over assigning undefined

doc.commonSchemas = undefined leaves an own property on the object that serialises to null in JSON.
Remove the key instead so it never leaks into the final spec.

-      commonSchemas: undefined,
+      // strip generator-specific option so it never appears in the output
+      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+      // @ts-ignore
+      delete (doc as any).commonSchemas,
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 89dd0d6 and b65f6de.

📒 Files selected for processing (4)
  • packages/openapi/src/openapi-generator.test.ts (2 hunks)
  • packages/openapi/src/openapi-generator.ts (15 hunks)
  • packages/openapi/src/openapi-utils.test.ts (2 hunks)
  • packages/openapi/src/openapi-utils.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/openapi/src/openapi-generator.test.ts
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: publish-commit
  • GitHub Check: lint

@dinwwwh dinwwwh merged commit bd95e77 into main Jun 13, 2025
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant