What is a monorepo?
A monorepo holds multiple projects — your client, your server, and shared packages like common types — in one repository. Turborepo manages the builds, caching, and task running across them. It is a natural fit for full-stack TypeScript, where client and server share code.
Why it matters
Sharing types and validation across the seam is far easier when client, server, and shared schemas live in one repo and one version. A monorepo makes a cross-cutting change atomic — one commit updates the shared type and both consumers together. Turborepo keeps the builds fast as it grows.
What to learn
- Workspaces and package structure
- A shared package for types and schemas
- Turborepo task pipelines and caching
- Internal dependencies between packages
- Running and building across the repo
- When a monorepo helps versus separate repos
- Avoiding over-engineering early
Common pitfall
Setting up an elaborate monorepo with many packages on day one, before there is anything to share, adding tooling complexity for no benefit. Start simple — even a single app — and introduce a shared package and Turborepo when you actually have code to share across client and server. Let the structure follow real need.
Resources
Primary (free):
- Turborepo — Documentation · docs
- Turborepo — Core concepts · docs
- pnpm — Workspaces · docs
Practice
Set up a small monorepo with a client app, a server app, and a shared package holding a Zod schema and its type, consumed by both. Configure a Turborepo pipeline so builds cache. Change the shared schema once and rebuild both. Done when one shared package serves both halves and builds are cached.
Outcomes
- Structure a monorepo with workspaces and a shared package.
- Share types and schemas across client and server.
- Use Turborepo pipelines and caching.
- Avoid over-engineering the structure too early.