Technical
Why we list Zod as a dependency and not as a peer dependency
Why we list Zod as a dependency and not as a peer dependency
Speakeasy-generated TypeScript SDKs use Zod for runtime validation to ensure correctness, provide safer defaults, and maintain type safety between API responses and TypeScript types. This validation layer catches mismatches between the OpenAPI specification and actual API behavior, preventing runtime errors in production.
Speakeasy includes zod as a regular dependency rather than a peer dependency. This design decision ensures better compatibility, simpler installation, and more reliable SDK behavior across different project configurations.
Understanding peer dependencies
Peer dependencies are designed to indicate that a package is a plugin or extension on top of another package
For example, a Babel plugin should be listed as a peer dependency because it makes no sense to install a Babel plugin if Babel isn’t already in the project. Similarly, zod-to-openapi lists Zod as a peer dependency because it’s a plugin that extends Zod’s functionality.
Why Speakeasy SDKs use dependencies
Speakeasy SDKs are not plugins on top of Zod. They are standalone libraries that use Zod internally for runtime validation. It makes perfect sense to install and use a Speakeasy SDK even if the project doesn’t otherwise use Zod.
Practical benefits
Whether using dependencies or peerDependencies, if an existing compatible version exists, npm will deduplicate installations. Also, starting with npm 7, npm automatically attempts to install peer dependencies. In many projects there’s little practical difference between dependencies and peerDependencies. However, in edge cases peerDependencies will result in confusing errors and incompatibility with certain projects:
Backward compatibility
If a project uses an older legacy version of Zod which is incompatible with our SDKs (such as Zod v2), the SDK will still work correctly with its own compatible Zod version installed.
With a peerDependency, the SDK would be forced to use the project’s older Zod version, which would break the SDK due to compatibility issues and preventing installation entirely.
Forward compatibility
If a project starts using Zod v5 in the future, and the SDK isn’t yet compatible with Zod v5, the SDK will continue to work correctly. A compatible version of Zod will be installed alongside the project’s Zod v5, and the SDK will use its own version.
With a peerDependency, the SDK would attempt to use the project’s Zod v5, potentially causing runtime errors if there are breaking changes.
No version conflicts
When multiple packages list React as a peerDependency with conflicting version requirements, npm throws an error because having multiple React versions in the same application causes problems. At time of writing, pnpm will just pick one of the versions and probably result in runtime issues.
However, in our case, having multiple Zod versions in a project is not problematic. Different packages can use different Zod versions without interfering with each other, since they don’t share global state or interact at runtime.
Simplified installation
Peer dependencies must be installed separately when using Yarn or earlier npm versions. With Zod as a regular dependency, SDK installation is a single step with no additional configuration required.
Version resolution
Whether Zod is listed as a dependency or peerDependency, if a compatible version of Zod is already installed in the project, that version will be used. Modern package managers deduplicate dependencies when possible, so listing Zod as a regular dependency doesn’t result in unnecessary duplication.
We aim to make our SDKs compatible with as broad a range of possible Zod versions to leverage this deduplication.
Summary
Listing Zod as a dependency rather than a peerDependency provides better compatibility, simpler installation, and more reliable SDK behavior while maintaining the same deduplication benefits.