How to Setup monorepo with Turborepo
Tags: webdev, javascript, monorepo, turborepo
Published: 2024-06-12 08:08:11


In this guide, we will create a monorepo using TurboRepo, integrating React, Express, and PNPM. We will also include shared code for UI components and functions. The process involves several steps. Here’s a detailed guide to help you set up this project:

### Step 1: Initialise the Monorepo


#### 1. Install PNPM (if not already installed):

```
npm install -g pnpm
```

#### 2. Create the Monorepo Directory:

```
mkdir turbo-monorepo
cd turbo-monorepo
```

#### 3. Initialise the Monorepo:

```
pnpm init
pnpm install -D turbo
```

#### 4. Setup Turbo Repo Configuration: Create a turbo.json file in the root directory:

```
{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**"]
    },
    "lint": {},
    "test": {}
  }
}
```

---

### Step 2: Setup Workspace and Packages


#### 1. Create Project Directories:

```
mkdir -p apps/client apps/server packages/ui packages/utils
```

#### 2. Configure pnpm-workspace.yaml:

```
packages:
  - 'packages/*'
  - 'apps/*'
```

---

### Step 3: Initialise Client (React) and Server (Express) Projects


#### 1. Client (React):

```
cd apps/client
pnpm create vite . --template react
pnpm install
cd ../..
```

#### 2. Server (Express):

```
cd apps/server
pnpm init -y
pnpm add express
touch index.js
cd ../..
```

#### 3. Add Basic Express Server Code in apps/server/index.js:

```
import express from "express"
import { greet } from "@shared/utils"
const app = express()
const port = 3000
app.get("/", (req, res) => {
  res.send(greet(" This is greeting from common code"))
})
app.listen(port, () => {
  console.log(`Server is running at http://localhost:${port}`)
})
```

---

### Step 4: Add Shared Packages


#### 1. UI Shared Package Setup:

```
cd packages/ui
pnpm init -y
touch index.js
cd ../..
```

#### 2. Change Name property of package.json to @shared/ui

```
{
  "name": "@shared/ui",
  "version": "1.0.0",
  "description": "",
  "main": "index.jsx",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}
```

#### 3. Example Shared Component in packages/ui/index.js:

```
import React from "react"
export const Button = ({ label }) => {
  return (
    <>
      <button type="reset" value="Reset" style={{ background: "red" }}>
        {label}
      </button>
    </>
  )
}
```


#### 1. Setup Shared Utils Package:

```
cd packages/utils
pnpm init -y
touch index.js
cd ../..
```

#### 2. Change Name property of package.json to @shared/utils

```
{
  "name": "@shared/utils",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}
```

#### 3. Example Shared Function in packages/utils/index.js:

```
export const greet = (name) => {
  return `Hello, ${name}!`
}
```

---

### Step 5: Link Shared Packages

Link Packages: (Not sure: This may be not required )

```
pnpm add @packages/ui @packages/utils --filter @apps/client
pnpm add @packages/utils --filter @apps/server
```

---

### Step 6: Update package.json Scripts


#### 1. Update Root package.json to add Script and workspace:

**Note:** Before this install concurrently: pnpm add -D concurrently

```
{
  "name": "turbo-monorepo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "private": true,
  "workspaces": [
    "apps/*",
    "packages/*"
  ],
  "scripts": {
    "build": "turbo run build",
    "lint": "turbo run lint",
    "test": "turbo run test",
    "start": "concurrently \"pnpm --filter @apps/server start\" \"pnpm --filter @apps/client dev\""
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "concurrently": "^8.2.2",
    "turbo": "^1.13.3"
  }
}
```

#### 2. Update Client to add  @shared/utils and @shared/ui in package.json:

```
{
  "name": "@apps/client",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "lint": "eslint . --ext js,jsx --max-warnings 0",
    "preview": "vite preview"
  },
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "@shared/utils": "workspace:*",
    "@shared/ui": "workspace:*"
  },
  "devDependencies": {
    "@types/react": "^18.2.66",
    "@types/react-dom": "^18.2.22",
    "@vitejs/plugin-react": "^4.2.1",
    "vite": "^5.2.0"
  }
}
```

#### 3. Update Server package.json to add @shared/utils dependency

```
{
  "name": "@apps/server",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "start": "node index.js",
    "lint": "eslint . --ext .js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.19.2",
    "@shared/utils": "workspace:*"
  }
}
```

---

### Step 7: Configure ESLint/Prettier/ VSCode Workspace in project root


#### 1. Install ESLint and Prettier:

```
pnpm add -Dw eslint prettier eslint-plugin-react eslint-config-prettier eslint-plugin-prettier
```

#### 2. Create ESLint Configuration (.eslintrc.json):

```
{
  "extends": ["eslint:recommended", "plugin:react/recommended", "prettier"],
  "plugins": ["react", "prettier"],
  "env": {
    "browser": true,
    "node": true,
    "es6": true
  },
  "parserOptions": {
    "ecmaVersion": 2020,
    "sourceType": "module"
  },
  "rules": {
    "react/prop-types": "off",
    "react/react-in-jsx-scope": "off",
    "prettier/prettier": [
      "error",
      {
        "endOfLine": "auto",
        "semi": false
      }
    ]
  },
  "settings": {
    "react": {
      "version": "detect"
    }
  }
}
```

#### 3. Create Prettier Configuration (.prettierrc.json):

```
{
  "singleQuote": false,
  "semi": false
}
```

#### 4. Setup VSCode Workspace : Create .vscode/settings.json:

```
{
  "editor.formatOnSave": true,
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact"
  ],
  "prettier.requireConfig": true
}
```

---

### Step 8: Start the Applications


Run the start script from the root of the monorepo:

```
pnpm start
 // OR
npm run start
```