Back to blog

Xây Dựng Ứng Dụng Full-Stack TypeScript với NPM Workspaces

typescriptexpressreactvitemonoreponpm-workspaces
Xây Dựng Ứng Dụng Full-Stack TypeScript với NPM Workspaces

Xây dựng ứng dụng full-stack với TypeScript mang lại trải nghiệm phát triển tuyệt vời—type safety xuyên suốt codebase, shared interfaces giữa frontend và backend, và bắt lỗi tại compile-time. Trong hướng dẫn này, chúng ta sẽ xây dựng ứng dụng blog hoàn chỉnh sử dụng kiến trúc monorepo với NPM workspaces.

Những Gì Bạn Sẽ Học

✅ Thiết lập NPM workspaces monorepo
✅ Chia sẻ TypeScript types giữa frontend và backend
✅ Xây dựng REST API với Express.js và TypeScript
✅ Tạo React frontend với Vite
✅ Cấu hình TypeScript project references
✅ Workflow phát triển cho monorepos
✅ Production build và deployment setup

Điều Kiện Tiên Quyết

📦 Full Source Code: Ví dụ hoàn chỉnh có sẵn trên GitHub: typescript-fullstack-demo


Ghi chú: Bài viết này chứa các ví dụ code tương tự phiên bản tiếng Anh. Chúng tôi tập trung vào giải thích khái niệm bằng tiếng Việt.

1. Tại Sao Monorepo với NPM Workspaces?

Vấn đề: Trong setup truyền thống, frontend và backend là các project riêng biệt với type definitions bị duplicate. Khi update backend API, bạn phải manually update frontend types—dễ lỗi và tốn thời gian.

Giải pháp: Monorepo với shared packages:

// packages/shared/src/types.ts (single source of truth)
export interface Post {
  id: number;
  title: string;
  content: string;
}
 
// Dùng trong cả backend VÀ frontend
import { Post } from '@blog/shared';

Lợi ích:

  • Single source of truth cho types
  • Atomic changes across packages
  • Simplified dependency management
  • Better code organization

2. Cấu Trúc Project

typescript-fullstack-demo/
├── packages/
│   ├── shared/              # Shared types và utilities
│   ├── server/              # Express.js backend
│   └── client/              # React + Vite frontend
├── package.json             # Root workspace configuration
└── README.md

3. Thiết Lập Monorepo

Root package.json:

{
  "name": "typescript-fullstack-demo",
  "private": true,
  "workspaces": ["packages/*"],
  "scripts": {
    "dev": "concurrently \"npm run server\" \"npm run client\"",
    "server": "npm run dev --workspace=@blog/server",
    "client": "npm run dev --workspace=@blog/client",
    "build": "npm run build --workspaces"
  }
}

4. Shared Package

Package chứa types được dùng bởi cả frontend và backend.

Key files:

  • packages/shared/src/types.ts - Common interfaces (Post, Comment, Category, ApiResponse)
  • packages/shared/tsconfig.json - Với composite: true cho project references

Types bao gồm:

  • Post, CreatePostRequest, UpdatePostRequest
  • Comment, CreateCommentRequest
  • Category
  • ApiResponse<T>, PaginatedResponse<T>

5. Express.js Backend

Cấu trúc:

  • src/config/database.ts - SQLite setup với auto table creation
  • src/models/Post.ts - Data model dùng types từ @blog/shared
  • src/controllers/postController.ts - Request handlers với typed responses
  • src/routes/postRoutes.ts - API routes
  • src/index.ts - Server entry point

TypeScript config với project references:

{
  "compilerOptions": {
    "paths": {
      "@blog/shared": ["../shared/src"]
    }
  },
  "references": [{ "path": "../shared" }]
}

API Endpoints:

  • GET /api/posts - Lấy tất cả posts
  • GET /api/posts/:id - Lấy post theo ID
  • GET /api/posts/search?q=query - Tìm kiếm posts
  • POST /api/posts - Tạo post mới
  • PUT /api/posts/:id - Update post
  • DELETE /api/posts/:id - Xóa post

6. React Frontend với Vite

Cấu trúc:

  • src/services/api.ts - API service với typed responses
  • src/components/PostList.tsx - Hiển thị danh sách posts
  • src/components/PostDetail.tsx - Chi tiết post với Markdown rendering
  • src/App.tsx - Main app với routing

Vite config với proxy và alias:

export default defineConfig({
  resolve: {
    alias: {
      '@blog/shared': path.resolve(__dirname, '../shared/src'),
    },
  },
  server: {
    port: 3000,
    proxy: {
      '/api': { target: 'http://localhost:5000' },
    },
  },
});

Features:

  • React Router cho client-side routing
  • Axios cho HTTP requests với typed responses
  • React Markdown với GitHub Flavored Markdown

7. Chạy Ứng Dụng

# Cài dependencies cho tất cả workspaces
npm install
 
# Build shared package trước
npm run build --workspace=@blog/shared
 
# Development mode (cả frontend và backend)
npm run dev
 
# Production build
npm run build
npm start

8. Lợi Ích Chính

Type Safety Across the Stack: Khi thay đổi type trong @blog/shared, TypeScript ngay lập tức bắt lỗi trong cả frontend và backend.

Single Dependency Installation: Một npm install ở root xử lý mọi thứ.

Consistent Tooling: Share configurations across packages (ESLint, Prettier, TypeScript).

9. Workspace Commands Reference

# Install dependency trong specific workspace
npm install axios --workspace=@blog/client
 
# Run script trong specific workspace
npm run dev --workspace=@blog/server
 
# Run script trong tất cả workspaces
npm run build --workspaces
 
# List tất cả workspaces
npm ls --workspaces

10. Troubleshooting

Types Not Updating: Rebuild shared package

npm run build --workspace=@blog/shared

Module Not Found: Reinstall từ root

rm -rf node_modules packages/*/node_modules
npm install

Summary

Trong hướng dẫn này, bạn đã học:

✅ Thiết lập NPM workspaces monorepo structure
✅ Tạo shared TypeScript packages cho type safety
✅ Xây dựng REST API với Express.js và TypeScript
✅ Tạo React frontend với Vite
✅ Cấu hình TypeScript project references
✅ Development và production workflows
✅ Troubleshooting common monorepo issues

Kiến trúc này scale tốt—bạn có thể thêm shared packages (@blog/utils, @blog/validation) và type safety tự động extends.

Bước Tiếp Theo

Giờ bạn đã có full-stack TypeScript monorepo, khám phá:

Mở Rộng Ứng Dụng:

  • Thêm user authentication với JWT
  • Implement pagination và filtering
  • Thêm image upload functionality
  • Deploy với Docker

Học Thêm:


Thuộc chuỗi bài Lộ Trình Học TypeScript

📬 Đăng ký nhận bản tin

Nhận những bài viết mới nhất qua email mỗi tuần. Không spam, hủy đăng ký bất cứ lúc nào.

Chúng tôi tôn trọng quyền riêng tư của bạn. Hủy đăng ký bất cứ lúc nào.

💬 Comments

Đăng nhập để bình luận

Chúng tôi sẽ không đăng bất cứ điều gì mà không có sự cho phép của bạn.