Monorepo
The Monorepo module leverages the power of Turborepo, enabling you to manage multiple packages and apps within a single repository. With this approach, you can efficiently organize your web development projects, streamline workflows, and enhance code maintainability.
Key Features
-
Monolithic Efficiency: Bring all your project packages into one unified workspace, eliminating the need for separate repositories.
-
Modular Organization: Keep your codebase organized by grouping related packages and modules within your monorepo.
-
Streamlined Development: Simplify code sharing, testing, and dependencies management across your project packages.
-
Codebase Consistency: Maintain code consistency and quality across all packages, making it easier to enforce best practices.
While these principles are necessary for tech teams, even solo makers benefit from them. By having code separated into packages, you avoid writing repetitive code.
The modularity brings clarity.
As seen in the Code Structure, there are
two main folders in the root of the codebase - apps
and packages
.
- apps
-- web
- packages
-- database
-- es-lint-config-custom
-- tailwind-config
-- tsconfig
-- ui
Apart from modularity, this structure allows you to deploy each app separately
(e.g. web
runs on the main domain, while docs
runs on a subdomain), which
gives you perfect control over the scalability of each app.
package.json
Every app defines its own package.json
with its own scripts, dependencies, and
devDependencies. It makes the package.json
files smaller and thus more
maintainable.
There is also a root package.json
file that defines project-scope dependencies
as well as things like packageManager
, workspaces
, and engines
.
turbo.json
You control how Turborepo behaves by defining turbo.json
files.
You can find one at the root of the project:
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": ["**/.env.*local"],
// Add every environment variable used in the repository (e.g. via process.env.VERCEL_URL)
"globalEnv": ["VERCEL_URL"],
// Every key in the "pipeline" relates to script names defined in "package.json" files
"pipeline": {
"build": {
"dependsOn": ["^db:generate", "^build"],
"outputs": [".next/**", "!.next/cache/**"]
},
// You can parallelize tasks that don't depend on each other
// https://turbo.build/repo/docs/core-concepts/monorepos/task-dependencies#dependencies-outside-of-a-task
"topo": {
"dependsOn": ["^topo"]
},
"lint": {
"dependsOn": ["topo"]
},
"ts": {
"dependsOn": ["topo"]
},
"dev": {
// This makes sure that "db:generate" runs before "dev" scripts.
"dependsOn": ["^db:generate"],
"cache": false,
"persistent": true
},
"db:generate": {
"cache": false
},
"db:push": {
"cache": false
}
}
}
You can also find turbo.json in a package/app itself:
{
"extends": ["//"],
"pipeline": {
"dev": {
// This needs to be defined on a package-level
"dependsOn": ["^content#build", "^db:generate"]
}
}
}
When you run pnpm run dev
, Turborepo runs all dev
scripts across the
repository (all packages and apps). You usually don't need all packages/apps to
run simultaneously, especially when you need to save your computer's resources.
For this purpose, you can run a package/app independently:
pnpm run dev --filter=web
Further reading
Read more about Turborepo in official docs.