Skip to content

fix(open-api): generate requestBody for intersection body schemas#9883

Closed
sanidhyasin wants to merge 2 commits into
better-auth:mainfrom
sanidhyasin:fix/open-api-intersection-request-body
Closed

fix(open-api): generate requestBody for intersection body schemas#9883
sanidhyasin wants to merge 2 commits into
better-auth:mainfrom
sanidhyasin:fix/open-api-intersection-request-body

Conversation

@sanidhyasin

@sanidhyasin sanidhyasin commented Jun 3, 2026

Copy link
Copy Markdown

Description

Endpoints whose request body is built with an intersection — z.object({...}).and(z.record(z.string(), z.any())) — produce a Zod ZodIntersection. The OpenAPI generator's getRequestBody only handled ZodObject / ZodOptional, so for these endpoints it fell through and returned undefined, emitting the operation without a requestBody.

The most visible case is POST /sign-in/email-otp (the emailOTP plugin), whose body is:

z.object({ email, otp, name?, image? }).and(z.record(z.string(), z.any()))

Its generated spec was missing the requestBody entirely.

Fix

Add a ZodIntersection branch to getRequestBody, backed by a small resolveIntersectionSchema helper that:

  • merges the defined object members into properties (preserving required for non-optional fields), and
  • represents a z.record(...) catch-all member as additionalProperties: true.

The existing ZodObject / ZodOptional path is left untouched, so no other operation's output changes. Endpoints that already declare an explicit metadata.openapi.requestBody (e.g. /sign-up/email) continue to short-circuit as before.

Tests

Added a regression test (open-api.test.ts) that registers the emailOTP plugin and asserts /sign-in/email-otp now has a requestBody with email/otp required, name optional, and additionalProperties: true.

Test Files  1 passed (1)
     Tests  13 passed (13)

pnpm typecheck and biome both pass.

Closes #9679

Note: the linked duplicate #9298 is closed but no fix has landed on main/next.


Summary by cubic

Fix the OpenAPI generator to include requestBody for intersection body schemas (z.object(...).and(z.record(...))), so endpoints like POST /sign-in/email-otp are documented correctly. Also dedupes required keys and preserves record value types in additionalProperties.

  • Bug Fixes
    • Handle ZodIntersection in getRequestBody via resolveIntersectionSchema.
    • Merge object members into properties with accurate, deduped required; map z.record(...) to additionalProperties using the processed value type (fallback to true for z.any()/z.unknown()).
    • Keep existing ZodObject/ZodOptional behavior and explicit metadata.openapi.requestBody overrides unchanged.
    • Add a regression test ensuring /sign-in/email-otp now emits a proper requestBody.

Written for commit ddaeae5. Summary will update on new commits.

Review in cubic

Endpoints whose body schema is built with `z.object({...}).and(z.record(...))`
produce a `ZodIntersection`, which the OpenAPI generator did not handle. As a
result `getRequestBody` returned `undefined` and the operation was emitted
without a `requestBody` (e.g. `/sign-in/email-otp`).

Resolve the intersection by merging its object members into `properties` and
representing a record catch-all as `additionalProperties`, so the request body
is documented as expected.

Closes better-auth#9679
Copilot AI review requested due to automatic review settings June 3, 2026 07:02
@sanidhyasin sanidhyasin requested a review from a team as a code owner June 3, 2026 07:02
@sanidhyasin sanidhyasin requested review from bytaesu and removed request for a team June 3, 2026 07:02
@vercel

vercel Bot commented Jun 3, 2026

Copy link
Copy Markdown

@sanidhyasin is attempting to deploy a commit to the better-auth Team on Vercel.

A member of the Team first needs to authorize it.

@better-release better-release Bot added the devtools CLI, OpenAPI, telemetry, i18n, test-utils label Jun 3, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR fixes OpenAPI generation for endpoints whose request body schema is a Zod intersection (notably z.object(...).and(z.record(...))), ensuring the resulting operations include a requestBody.

Changes:

  • Add OpenAPI generator support for ZodIntersection request bodies by flattening intersected object properties and representing records via additionalProperties.
  • Add a regression test covering /sign-in/email-otp to ensure requestBody is generated and contains expected fields.
  • Add a changeset documenting the patch-level behavior change.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
packages/better-auth/src/plugins/open-api/generator.ts Adds intersection-aware requestBody generation via resolveIntersectionSchema.
packages/better-auth/src/plugins/open-api/open-api.test.ts Adds a regression test verifying requestBody generation for intersection schemas.
.changeset/open-api-intersection-request-body.md Documents the patch change in release notes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/better-auth/src/plugins/open-api/generator.ts
Comment thread packages/better-auth/src/plugins/open-api/generator.ts

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

1 issue found across 3 files

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread packages/better-auth/src/plugins/open-api/generator.ts
Address review feedback on intersection request-body generation:

- Deduplicate `required` so intersecting objects that share a property name
  don't emit duplicate keys (JSON Schema requires them to be unique).
- Map a record member to its processed value-type schema instead of always
  `additionalProperties: true`, preserving type info (e.g. `z.record(z.string(),
  z.number())` -> `additionalProperties: { type: "number" }`). `true` is still
  used for unconstrained value types (`z.any()`/`z.unknown()`).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

devtools CLI, OpenAPI, telemetry, i18n, test-utils

Projects

None yet

Development

Successfully merging this pull request may close these issues.

/sign-in/email-otp OpenAPI spec missing requestBody

3 participants