You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@colinhackshinted at a future plugin API for z.toJSONSchema to replace ad-hoc parameter additions:
Very allergic to additional params on this function. I'm considering a plugin API that would give Zod the ability to encapsulate some of these common overrides and provides them to users via a consumable API.
This discussion is to put a concrete shape in front of that idea so it has something to react to, rather than starting from scratch when it's time to build it.
Motivating use cases
Three open issues all reduce to "downstream consumer doesn't accept a JSON Schema keyword Zod legitimately emits":
Each can be solved today with a hand-rolled override, but every project hitting the issue rewrites the same logic, and the post-walk approach has minor footguns (the path arg in nested overrides references the original keyword).
Proposed shape
A plugin is a value of type JSONSchemaGeneratorParams — i.e. the same options bag toJSONSchema already accepts. Plugins compose by deep-merging override (chained) and shallow-merging the rest.
Is JSONSchemaPlugin = JSONSchemaGeneratorParams & { name? } close to what you had in mind, or were you thinking of something with a richer surface (lifecycle hooks, ordering control, etc.)?
Do built-in plugins live under z.plugins.*, a separate import (zod/plugins), or stay external entirely?
Should withPlugins be exposed (z.toJSONSchema.with(...) chainable, or z.plugins.compose(...)), or do callers just spread plugin objects manually into the params bag?
If the shape is roughly right, happy to draft a PR. If not, useful to know what to pivot to before sinking time.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
-
Context
@colinhacks hinted at a future plugin API for
z.toJSONSchemato replace ad-hoc parameter additions:This discussion is to put a concrete shape in front of that idea so it has something to react to, rather than starting from scratch when it's time to build it.
Motivating use cases
Three open issues all reduce to "downstream consumer doesn't accept a JSON Schema keyword Zod legitimately emits":
z.union(...)→oneOfinstead ofanyOf(Gemini rejectsanyOf)oneOffor inclusive unionsEach can be solved today with a hand-rolled
override, but every project hitting the issue rewrites the same logic, and the post-walk approach has minor footguns (thepatharg in nested overrides references the original keyword).Proposed shape
A plugin is a value of type
JSONSchemaGeneratorParams— i.e. the same options bagtoJSONSchemaalready accepts. Plugins compose by deep-mergingoverride(chained) and shallow-merging the rest.Usage:
Why this shape
JSONSchemaGeneratorParams. No new type to learn; users already know the options bag.withPluginsis opt-in —toJSONSchemaitself stays untouched. Users who don't need plugins never encounter them.overridecallbacks chain in declaration order, other fields shallow-merge with last-wins semantics.z.plugins.<name>keeps discovery cheap from the IDE without bloating the top-level export.First built-in:
z.plugins.geminiCompatCould be expanded later (Gemini also rejects some other keywords) without breaking the call site.
Possible follow-up plugins
z.plugins.swagger2()— Swagger 2 / OpenAPI 2 compatibility (noanyOf, noexamples, etc.).z.plugins.openaiStructuredOutput()— OpenAI's structured-output strict mode.z.plugins.draft07()— already covered bytarget, but could fold in legacy quirks consumers expect.Questions for @colinhacks
JSONSchemaPlugin = JSONSchemaGeneratorParams & { name? }close to what you had in mind, or were you thinking of something with a richer surface (lifecycle hooks, ordering control, etc.)?z.plugins.*, a separate import (zod/plugins), or stay external entirely?withPluginsbe exposed (z.toJSONSchema.with(...)chainable, orz.plugins.compose(...)), or do callers just spread plugin objects manually into the params bag?If the shape is roughly right, happy to draft a PR. If not, useful to know what to pivot to before sinking time.
Beta Was this translation helpful? Give feedback.
All reactions