CodeNewbie Community 🌱

Cover image for Building a Better Monorepo with TypeScript, Turborepo, or Nx
Nandani Sharma
Nandani Sharma

Posted on • Originally published at canopas.com

Building a Better Monorepo with TypeScript, Turborepo, or Nx

Introduction

Monorepo is a software development strategy that creates shared libraries and dependencies in a single repository. Monorepos offers a solution for crucial dependency management by centralizing codebases, improving collaboration, and streamlining dependency management.

This article aims to explain the process of creating monorepos using well-known tools and technologies like TypeScript, Turborepo, and Nx, allowing you to choose the best tool based on your requirements.

Why Monorepo?

Imagine managing three separate applications for a project: a Web App, a Mobile App, and an Admin Panel. Each resides in its git repository and shares a common UI component library. Now think, when you need to update a critical button component for accessibility.

The process becomes a headache:

  • Update the component library and publish to NPM
  • Update each application one by one
  • Deal with version conflicts and dependencies
  • Ensure consistency across all projects

Sounds frustrating, right? This is where monorepos comes in—a simpler approach where all your projects live under one roof, making it easier to coordinate changes and maintain consistency across your codebase.

Let’s start with monorepos…

In the following sections, we will create basic utils monorepo which can be used anywhere in the project.

Monorepo with Typescript

In this section, we will build a monorepo manually using just TypeScript and npm.

Initialize the Project

  • Create a monorepo example directory and set the root project using the below commands,
npm init -y
npm install typescript
npm install --save-dev @types/node ts-node
Enter fullscreen mode Exit fullscreen mode

Organize Packages

Create a packages folder and a utils directory within it to act as a shared library.

mkdir -p packages/utils
Enter fullscreen mode Exit fullscreen mode

Init npm inside utils with scope @monorepo,

npm init --scope @monorepo --workspace ./packages/utils -y
Enter fullscreen mode Exit fullscreen mode

packages/utils/package.json will look like this,

{
  "name": "@monorepo/utils",
  "version": "1.0.0",
  "main": "index.js",
  "devDependencies": {},
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": ""
}
Enter fullscreen mode Exit fullscreen mode

This will also add a workspace field in the root’s package.json like below. If you have multiple monorepos, you can update workspace using wildcards like "packages/*".

"workspaces": [
    "packages/utils" or "packages/*"
]
Enter fullscreen mode Exit fullscreen mode

Configure TypeScript

We need to configure typescript in both the directory root and packages,

npx tsc --init && cd packages/utils && npx tsc --init
Enter fullscreen mode Exit fullscreen mode

You can update baseDir and rootDir in packages/utils’s tsconfig.jsonas needed. In my case it is,

"rootDir": "./src",
"outDir": "./dist", // make sure to update main
Enter fullscreen mode Exit fullscreen mode

Note: Make sure to update the main field in the package.json based on your outDir. For me, it is dist/index.js .

  • As we need monorepo to be referenced in other projects, we need to add composite: true in utils/tsconfig.json .
  • Add utils reference in the project root’s tscofig.json
"compilerOptions": {...},
"references": [
     {"path": "./packages/utils" },
 ]
Enter fullscreen mode Exit fullscreen mode

For module resolution and path mapping, we will add the following in the root’s tsconfig.json

"compilerOptions": {
  ...,
  "baseUrl": "./",              
  "paths": {
      "@monorepo/utiltest": ["packages/utils/*"],
   }
},
Enter fullscreen mode Exit fullscreen mode

Add and Build Code

Write a sample function in packages/utils/src/index.ts

// packages/utils/src/index.ts

export function multiply(a: number, b: number): number {
    return a * b;
}
Enter fullscreen mode Exit fullscreen mode

Add "build": "tsc --build" in the root and utils package.json scripts, then build with npm run build.

Usage

  • Import @monorepo/utils into the root file to verify the function works as expected.
npm install @monorepo/utils
Enter fullscreen mode Exit fullscreen mode
  • You can use the monorepo package like below,
import { multiply } from "@monorepo/utils";

const result = multiply(2, 3);
console.log(`result: ${result}`);
Enter fullscreen mode Exit fullscreen mode

Yeah, You have created a basic monorepo setup. You can build as many as monorepos and use them like this.

When to Use?

Monorepo’s with only typescript is useful for small, simple projects or experimental purposes without requiring third-party tooling.

Next, let’s create monorepo with Turborepo.


Monorepo with Turborepo

Turborepo is a high-performance monorepo build tool that simplifies dependency management and speeds up builds through caching.

Follow the below steps to configure monorepo using Turborepo.

Set Up Turborepo

Install Turborepo as a dev dependency,

npm install turbo -D
Enter fullscreen mode Exit fullscreen mode
  • Add packageManager to each package.json to ensure compatibility with Turborepo.
"packageManager": "npm@<npm-version>",
Enter fullscreen mode Exit fullscreen mode

Configure Turborepo

Create a turbo.json in the root directory:

{
    "$schema": "https://turborepo.org/schema.json",
    "tasks": {
      "build": {
        "dependsOn": ["^build"],
        "outputs": ["dist/**"]
      },
      "dev": {
        "cache": false
      }
    }
  }
Enter fullscreen mode Exit fullscreen mode

The tasks section defines tasks like build dependencies and caching behavior.

Add and Build Code

  • Add a data-transform package to illustrate Turborepo's dependency management.
  • Add code for transforming data in data-transform,
export function transformData(data: any): any {   
   return data.map((item) => ({ ...item, transformed: true })); 
}
Enter fullscreen mode Exit fullscreen mode

Build the entire project using,

npx turbo run build
Enter fullscreen mode Exit fullscreen mode
  • Turborepo automatically caches results, speeding up subsequent builds.

Usage

You can install @monorepo/data-transform into the root or utils package, and verify it's working as expected.

When to Use?

Ideal for mid-size projects that need optimized build times and clear dependency management.


Yeah! We've Successfully Built a Monorepo with TypeScript and Turborepo!

Ready to take it a step further with Nx? 🚀

Head over to our full blog to explore how to implement Monorepo with Nx and discover even more insights on effective monorepo management.


If you like what you read, be sure to hit 💖 button! — as a writer it means the world!

I encourage you to share your thoughts in the comments section below. Your input not only enriches our content but also fuels our motivation to create more valuable and informative articles for you.

Happy coding! 👋

Top comments (1)

Collapse
 
henrich45 profile image
henrich45

Monorepos simplify dependency management, making them ideal for collaboration and scalability. Tools like TypeScript, Turborepo, and Nx streamline the setup process, ensuring efficient workflows. For projects like "becabenitojuarez," adopting a monorepo strategy can centralize codebases, improve maintainability, and accelerate development. Choose the tool that aligns best with your project's needs.