Nx Workspace là gì? — Giới thiệu và hướng dẫn setup monorepo từ đầu

Bắt đầu thứ Hai với ticket “thêm tính năng auth vào app” — nghe đơn giản. Nhưng project đang có: một React frontend, một Node.js API, một package shared types dùng chung, và một admin dashboard riêng. Bốn repo khác nhau. Bốn lần npm install. Bốn pipeline CI/CD. Và khi bạn update kiểu dữ liệu trong shared types, bạn phải nhớ đồng bộ thủ công sang từng repo còn lại — và thường xuyên quên.

Đây là bài toán mà Nx Workspace được sinh ra để giải quyết. Không phải chỉ là “để code vào một chỗ” — mà là biến một monorepo từ thứ nghe có vẻ phức tạp thành thứ hoạt động nhanh hơn, nhất quán hơn, và có thể scale được khi team lớn lên.

Nx là gì?

Nx là một build system và monorepo management tool mã nguồn mở, được xây dựng bởi Nrwl — công ty do hai cựu engineer của Google thành lập năm 2016. Ban đầu chỉ hỗ trợ Angular, nhưng giờ Nx hỗ trợ hầu hết mọi stack: React, Vue, Next.js, NestJS, Node.js, và ngay cả Go hay Java thông qua plugin ecosystem.

Nếu monorepo là “để tất cả dự án trong một repo” thì Nx là thứ làm cho cái repo đó thực sự hoạt động được:

Hãy hình dung Nx như một traffic controller thông minh cho codebase của bạn: nó biết chính xác project nào phụ thuộc vào project nào, task nào cần chạy trước, kết quả nào đã được cache, và chỉ chạy đúng những gì cần thiết — không hơn, không kém.

Nx khác gì Turborepo?

Câu hỏi hay gặp nhất. Cả hai đều là monorepo tool với caching và task orchestration — nhưng có sự khác biệt về triết lý:

TurborepoNx
Triết lýLightweight, ít opinionatedFull-featured, có kiến trúc rõ ràng
Code generationKhông có✅ Mạnh — generators cho mọi thứ
Plugin ecosystemHạn chế✅ 100+ official + community plugins
Project graphCơ bản✅ Interactive visualization
Phù hợp nhấtProject nhỏ, Vercel ecosystemProject vừa-lớn, cần structure
Learning curveThấp hơnCao hơn — nhưng đáng

Tóm gọn: Turborepo là high-speed engine, Nx là full vehicle sẵn sàng cho production. Nếu có 50+ package trong monorepo hoặc cần distributed execution — Nx là lựa chọn rõ ràng hơn.

Các khái niệm cốt lõi cần hiểu trước

  • Workspace: Toàn bộ monorepo — thư mục root chứa tất cả apps, libs và config chung.
  • App: Đơn vị có thể deploy được — React app, Node API, mobile app. Thường phụ thuộc vào libs nhưng không có app nào phụ thuộc vào nó.
  • Lib: Đơn vị code có thể tái sử dụng — UI components, shared utils, data access layer, shared types. Được import bởi apps hoặc libs khác.
  • Project Graph: Đồ thị dependency giữa tất cả projects trong workspace — Nx dùng cái này để tính toán task execution order và affected projects.
  • Affected: Tính năng core của Nx — khi bạn thay đổi code, Nx tính toán project nào bị ảnh hưởng và chỉ build/test những project đó. Đây là thứ giữ CI pipeline nhanh dù monorepo lớn.
  • Cache: Nx cache kết quả của mọi task theo input. Nếu code không thay đổi, task không cần chạy lại — dùng cached result ngay lập tức.
  • Generator: Script tạo code tự động theo template — tạo app mới, lib mới, hay component mới theo đúng convention của project.

Setup Nx Workspace từ đầu

Bước 1: Tạo workspace mới

Chạy lệnh sau và làm theo prompts:

npx create-nx-workspace@latest my-workspace

# Nx sẽ hỏi một số câu:
# ✔ Which stack do you want to use? → None (sẽ thêm sau)
# ✔ Package-based or integrated? → Integrated
# ✔ Enable Nx Cloud? → Tùy (free tier có remote cache)

cd my-workspace

Cấu trúc được tạo ra:

my-workspace/
├── apps/              ← Apps sẽ nằm ở đây
├── libs/              ← Shared libraries
├── nx.json            ← Cấu hình Nx (caching, task runners...)
├── package.json       ← Root package.json
└── tsconfig.base.json ← TypeScript path aliases dùng chung

Bước 2: Thêm React app đầu tiên

Cài plugin React và generate app:

# Cài Nx React plugin
npm install --save-dev @nx/react

# Generate React app
npx nx g @nx/react:application --name=web --directory=apps/web

# Nx sẽ hỏi:
# ✔ Bundler? → Vite (recommended) hoặc Webpack
# ✔ Stylesheet format? → CSS / SCSS / Tailwind...

Cấu trúc sau khi thêm app:

apps/
└── web/
    ├── src/
    │   ├── app/
    │   │   ├── app.tsx
    │   │   └── app.spec.tsx
    │   └── main.tsx
    ├── project.json     ← Cấu hình targets của project này
    ├── vite.config.ts
    └── tsconfig.json

Bước 3: Thêm Node.js API

# Cài Nx Node plugin
npm install --save-dev @nx/node

# Generate Node app (Express)
npx nx g @nx/node:application --name=api --directory=apps/api

# Hoặc nếu dùng NestJS
npm install --save-dev @nx/nest
npx nx g @nx/nest:application --name=api --directory=apps/api

Bước 4: Tạo shared library

Đây là phần làm cho monorepo thực sự có giá trị. Tạo một lib chứa shared TypeScript types dùng chung cho cả frontend lẫn backend:

# Tạo shared types library
npx nx g @nx/js:library --name=shared-types --directory=libs/shared/types

# Tạo shared UI components library
npx nx g @nx/react:library --name=ui --directory=libs/shared/ui

Sau khi tạo, tsconfig.base.json tự động được update với path alias:

// tsconfig.base.json
{
  "compilerOptions": {
    "paths": {
      "@my-workspace/shared-types": ["libs/shared/types/src/index.ts"],
      "@my-workspace/ui": ["libs/shared/ui/src/index.ts"]
    }
  }
}

Giờ bạn import shared code một cách sạch sẽ trong cả hai app:

// Trong React app
import { User, Order } from '@my-workspace/shared-types'
import { Button, Modal } from '@my-workspace/ui'

// Trong Node API — cùng một import path
import { User, Order } from '@my-workspace/shared-types'

Không cần publish lên npm, không cần symlink thủ công, không cần copy-paste types giữa các project. Thay đổi type một chỗ, TypeScript báo lỗi ở mọi nơi đang dùng nó ngay lập tức.

Các lệnh Nx hay dùng nhất

Chạy tasks cơ bản

# Build một project cụ thể
npx nx build web
npx nx build api

# Chạy dev server
npx nx serve web
npx nx serve api

# Chạy test
npx nx test web
npx nx test shared-types

# Chạy lint
npx nx lint web

# Chạy nhiều targets cùng lúc (parallel)
npx nx run-many -t build test --parallel=3

Affected — Chỉ chạy những gì thay đổi

Đây là tính năng tiết kiệm thời gian nhất khi dùng Nx trong CI:

# Build chỉ những project bị ảnh hưởng bởi changes hiện tại
npx nx affected -t build

# Test chỉ những project bị ảnh hưởng
npx nx affected -t test

# Lint chỉ những project bị ảnh hưởng
npx nx affected -t lint

# Xem project nào bị ảnh hưởng (không chạy gì)
npx nx affected:graph

Ví dụ thực tế: bạn sửa code trong libs/shared/types. Nx biết rằng apps/webapps/api đều import lib đó — nên nx affected -t test sẽ chạy test cho cả ba project. Nhưng nếu bạn chỉ sửa UI component trong libs/shared/ui, Nx biết API không import nó — nên chỉ test web app và ui lib, bỏ qua API hoàn toàn.

Project Graph — Visualize dependencies

# Mở interactive dependency graph trong browser
npx nx graph

Lần đầu chạy lệnh này, bạn sẽ thấy toàn bộ dependency graph của workspace hiển thị dạng interactive — click vào node để xem dependencies, filter theo project, hay focus vào một sub-graph. Đặc biệt hữu ích khi onboard member mới hoặc debug circular dependency.

Nx Cache — Không chạy lại thứ đã chạy

# Lần đầu build — chạy thật
npx nx build web
# > NX  Successfully ran target build for project web (12s)

# Lần hai — code không thay đổi → dùng cache
npx nx build web
# > NX  Successfully ran target build for project web
# [local cache] — 0ms

Cache được lưu local theo mặc định. Nếu bật Nx Cloud (có free tier), cache được share với toàn bộ team — developer A build xong, developer B chạy cùng task sẽ dùng cache của A mà không cần chạy lại.

Cấu hình nx.json quan trọng

// nx.json
{
  "defaultBase": "main",           // So sánh affected với branch nào
  "targetDefaults": {
    "build": {
      "dependsOn": ["^build"],      // Build libs trước khi build app dùng chúng
      "cache": true                 // Cache kết quả build
    },
    "test": {
      "cache": true
    },
    "lint": {
      "cache": true
    }
  },
  "namedInputs": {
    "default": ["{projectRoot}/**/*", "sharedGlobals"],
    "sharedGlobals": ["{workspaceRoot}/tsconfig.base.json"]
  }
}

Dòng "dependsOn": ["^build"] là quan trọng nhất — nó nói với Nx rằng “trước khi build app này, hãy build tất cả dependencies của nó trước”. Ký hiệu ^ có nghĩa là “các dependencies”, không phải project hiện tại.

Tích hợp CI/CD — Chỉ build những gì thay đổi

Đây là nơi Nx tỏa sáng nhất trong môi trường team. Thay vì build và test toàn bộ monorepo mỗi lần có PR, chỉ xử lý những project bị ảnh hưởng:

# .github/workflows/ci.yml
name: CI
on: [push, pull_request]

jobs:
  main:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0          # Cần full history để tính affected

      - uses: actions/setup-node@v4
        with:
          node-version: 20

      - run: npm ci

      # Set SHAs để Nx biết so sánh với commit nào
      - uses: nrwl/nx-set-shas@v4

      # Chỉ lint, test, build những project bị affected
      - run: npx nx affected -t lint test build --parallel=3

Kết quả thực tế: CI pipeline của một monorepo có 20 projects thường chạy trong 3-5 phút thay vì 20-30 phút — vì mỗi PR thường chỉ thay đổi 2-3 projects, không phải tất cả.

Điểm mới đáng chú ý năm 2025-2026

TypeScript Project References — Setup mới được recommend

Nx vừa ra mắt setup mới kết hợp npm/pnpm/yarn workspaces với TypeScript project references thay vì TypeScript path aliases như trước. Setup này nhanh hơn, ít tốn memory hơn, và editor support tốt hơn cho large monorepo. Đây là recommended approach cho workspace mới tạo từ 2025 trở đi.

Nx MCP Server — AI hiểu codebase của bạn

Điểm giao thoa thú vị với AI workflow: Nx MCP Server cho phép AI assistants (Claude, Cursor…) hiểu được workspace structure, project dependencies, và task configurations của bạn. Thay vì AI làm việc “mù” không biết project graph của bạn trông như thế nào, nó có full context để đưa ra gợi ý chính xác hơn.

Self-healing CI

Khi PR fail, Nx phân tích lỗi, xác định root cause, và post fix suggestion trực tiếp vào PR. Trong nhiều case, nó có thể tự commit fix luôn — CI tiếp tục chạy mà không cần developer can thiệp. Khoảng 60% broken PRs nhận được effective fix từ tính năng này.

Khi nào KHÔNG nên dùng Nx?

Nx không phải là câu trả lời cho mọi project:

  • Project nhỏ, một app duy nhất: Overhead của Nx không đáng — setup thêm phức tạp mà không có benefit rõ ràng
  • Team chưa quen monorepo: Learning curve của Nx cộng với learning curve của monorepo sẽ tốn nhiều thời gian ban đầu — cân nhắc kỹ
  • Cần ship nhanh, chưa biết scope: Bắt đầu với separate repos, migrate sang monorepo sau khi đã rõ ràng cần share code gì — Nx hỗ trợ nx init để migrate dần
  • Team nhỏ hơn 3 người: Lợi ích của shared code và affected commands chỉ thực sự rõ khi team và codebase đủ lớn

Tổng kết

Nx không chỉ là “để code vào một repo” — nó là infrastructure giúp monorepo hoạt động ở tốc độ của polyrepo nhờ caching và affected commands, đồng thời giữ được tất cả lợi ích của shared code và atomic changes.

Tóm lại những gì cần nhớ:

  • Nx = build system + monorepo tool — caching, affected commands, code generation, project graph
  • Apps là những gì deploy được, Libs là code dùng chung — đây là phân biệt quan trọng nhất
  • nx affected -t build test là lệnh cần biết nhất cho CI — chỉ xử lý những gì thực sự thay đổi
  • Cache tự động — task đã chạy với cùng input sẽ không chạy lại, dùng cached result
  • Nx vs Turborepo: Turborepo cho project nhỏ muốn quick start, Nx cho project vừa-lớn cần structure và plugin ecosystem
  • Không phù hợp khi project nhỏ hoặc team chưa sẵn sàng với monorepo overhead

Nếu bạn đang có 2+ project share code với nhau và đang phải sync thủ công — đó là dấu hiệu rõ nhất để thử Nx. Chạy npx create-nx-workspace@latest, dành một buổi chiều setup, và bạn sẽ thấy ngay sự khác biệt khi thay đổi shared type và toàn bộ workspace báo lỗi đúng chỗ ngay lập tức.

Chúc anh em code vui! 🚀


Tags: #nx #monorepo #workspace #react #nodejs #typescript #cicd #devops #frontend #backend

1 Comment

  1. Bắt đầu thứ Hai với ticket “thêm tính năng auth vào app” — nghe đơn giản. Nhưng project đang có: một React frontend, một Node.js API, một package shared types dùng chung, và một admin dashboard riêng. Bốn repo khác nhau. Bốn lần npm install. Bốn pipeline CI/CD. Và khi bạn update kiểu dữ liệu trong shared types, bạn phải nhớ đồng bộ thủ công sang từng repo còn lại — và thường xuyên quên.

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *