Tips for Integrating a TypeScript SDK into a Monorepo
Get Started
If you’re looking to set up a monorepo from scratch, check out our Create a monorepo guide first.
Dependency Confusion
The most common issue we see is dependency confusion. This happens when developers have multiple versions of the same dependency across different packages in the monorepo.
Why is this a problem?
Let’s say a developer have Zod installed in their monorepo’s root, and their Speakeasy SDK also uses Zod. They might run into a situation where code like this doesn’t work:
The instanceof
check fails because they’re importing ZodError
from a different instance of Zod than what the SDK is using. It’s the same code, but JavaScript treats them as different classes.
How to fix it
PNPM’s strict module resolution is fantastic at preventing this issue:
If you’re using Yarn or npm, you’ll need to be more explicit:
This forces all packages to use the same version of these dependencies, preventing the confusion.
Module format mismatches - ESM vs CommonJS chaos
The second most common issue is dealing with mixed module formats. Some packages use CommonJS, others use ESM, and a monorepo probably has a mix of both.
Why this is a problem
You might see errors like:
Or:
These happen when module systems don’t align. It’s especially common when there’s a package using CommonJS trying to import an ESM module, or vice versa.
How to fix it
The simplest solution is to configure your Speakeasy SDK to use ESM format:
If you need to support both ESM and CommonJS packages importing your SDK, you can use the dual format (which is the Speakeasy default):
When using moduleFormat: dual
, make sure the tsconfig.json is set up correctly:
There’s a small bundle size tradeoff with dual format, but it’s usually worth it for the compatibility benefits.
Package manager differences - npm vs the world
The last common issue is package manager compatibility. Speakeasy uses npm when building SDKs, but a monorepo might use pnpm, Yarn, or even Bun.
Why this is a problem
Different package managers handle dependencies differently. This can lead to subtle issues where the SDK works fine when generated, but breaks when integrated into the monorepo.
For example, one might see errors about missing dependencies that they know are installed, or weird resolution issues that only happen in the monorepo.
How to fix it
Configure Speakeasy to use your preferred package manager when building the SDK. For pnpm (recommended for monorepos), customize the compile command in your gen.yaml
:
This tells Speakeasy to use pnpm instead of npm when building the SDK, which is especially important in monorepos where pnpm’s strict module resolution helps prevent dependency confusion issues.
Related Guide
For more detailed information about configuring pnpm as your default package manager, see our Using PNPM guide.
Putting it all together - a real-world example
This is an example of how a developer may set up a monorepo with a Speakeasy SDK:
pnpm-workspace.yaml:
frontend/package.json:
gen.yaml:
This setup gives:
- A consistent dependency tree with pnpm’s strict module resolution
- ESM modules for maximum compatibility
- pnpm for package management
Best practices for TypeScript SDKs in monorepos
Beyond the specific issues above, here are some general best practices:
- Use workspace references: Always reference your SDK using workspace syntax (
workspace:*
) rather than local file paths - Consistent TypeScript versions: Use the same TypeScript version across all packages
- Shared tsconfig: Create a base tsconfig.json that all packages extend
- Centralized types: Consider creating a shared types package for common interfaces
- Integration tests: Write tests that verify the SDK works correctly with other packages
Related documentation
For more information on configuring Speakeasy TypeScript SDKs, check out:
- TypeScript SDK Design
- Configuring Module Format
- TypeScript SDK Reference
- Model Validation and Serialization
- TypeScript Configuration
Last updated on