1. Bắt Đầu Dự Án Web Với NestJS, NextJS Và PNPM Workspace
Thiết Lập Workspace
Đầu tiên, tạo một thư mục tên là simple-portfolio và bắt đầu từ đây. Đừng quên khởi tạo git để quản lý mã nguồn và tạo một tệp .gitignore với nội dung sau:
# simple-portfolio
git init
touch .gitignore
// simple-portfolio/.gitignore
node_modules
dist
build
.env
Để bắt đầu chúng ta cần tạo một workspace mới với pnpm.
# simple-portfolio
touch pnpm-workspace.yaml
Pnpm workspace cho phép chúng ta cấu trúc mã nguồn thành một monorepo để dễ dàng quản lý, chạy, build, và deploy cả mã nguồn back-end lẫn front-end.
Hiện tại, có khá nhiều công cụ monorepo tốt hơn với nhiều chức năng hơn (như Nx), nhưng vì đây là một dự án nhỏ nên mình sử dụng pnpm workspace cho đơn giản.
Thiết lập nội dung tệp pnpm-workspace.yaml để các ứng dụng được tạo ra trong thư mục apps được nhận diện.
# simple-portfolio/pnpm-workspace.yaml
packages:
- "apps/*"
Tạo Back-End Server Với NestJS
Tạo thư mục apps, di chuyển vào đó và chạy khởi tạo ứng dụng NestJS mới tên server.
# simple-portfolio/apps
nest new server --strict --skip-git --package-manager=pnpm
Mặc định NestJS được thiết lập listen port 3000, điều này gây conflict port với ứng dụng NextJS mà chúng ta sắp tạo. Hãy chuyển nó thành port 4000 và chạy thử.
// simple-portfolio/apps/server/src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(4000); // change this
}
bootstrap();
# simple-portfolio/apps/server
pnpm start:dev
Vậy là chúng ta đã tạo xong phần back-end server. Tiếp đến ta bắt đầu tạo phần front-end. Trở về thư mục apps và tạo 2 ứng dụng NextJS lần lượt là office và client.
Tạo Front-End: Ứng Dụng Office và Client Với NextJS
# simple-portfolio/apps
pnpx create-next-app@latest office --ts --eslint --tailwind --app --use-pnpm
# simple-portfolio/apps
pnpx create-next-app@latest client --ts --eslint --tailwind --app --use-pnpm
Mặc định NextJS được thiết lập listen port 3000 nên 2 app vừa tạo sẽ bị conflict. Hãy chỉnh lại port trên app office thành 3500.
// simple-portfolio/apps/office/package.json
{
"name": "office",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev -p 3500", // change this
"build": "next build",
"start": "next start",
"lint": "next lint"
},
...
}
Kết Nối và Cấu Hình Các Ứng Dụng
Chúng ta đã tạo xong 3 app nhưng hiện tại chúng vẫn đang rời rạc chưa được kết nối và chưa thể khởi chạy cùng lúc.
Mở Rộng tsconfig Để Chia Sẻ Code
Một trong những lợi ích của monorepo là các app có thể chia sẻ code với nhau (thường gặp nhất là front-end và back-end chia sẻ các interface cho việc truyền nhận dữ liệu).
Để làm được điều này, hãy tạo 1 file tsconfig.json ở root và config như sau:
# simple-portfolio
touch tsconfig.json
// simple-portfolio/tsconfig.json
{
"compilerOptions": {
"incremental": true,
"skipLibCheck": true,
"paths": {
"@server/*": ["./apps/server/src/*"],
"@office/*": ["./apps/office/*"],
"@client/*": ["./apps/client/*"]
}
}
}
Config trên khai báo các paths để chúng ta có thể dễ dàng import trong code. Đồng thời cần sửa các file tsconfig.json ở 3 app để chúng extend config vừa tạo.
Tại app server chúng ta config như sau:
// simple-portfolio/apps/server/tsconfig.json
{
"extends": "../../tsconfig.json", // add this
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "ES2021",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"strictNullChecks": true,
"noImplicitAny": true,
"strictBindCallApply": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true
}
}
Tại app office và client thay đổi config thành:
// simple-portfolio/apps/office/tsconfig.json
// simple-portfolio/apps/client/tsconfig.json
{
"extends": "../../tsconfig.json", // add this
"compilerOptions": {
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
Chạy Đồng Thời Tất Cả Các Ứng Dụng
Để các app có thể khởi chạy cùng lúc, script khởi chạy trong package.json của các app cần được thiết lập giống nhau.
Trên môi trường development, chúng ta sẽ cùng sử dụng script dev cho app office và client. Tuy nhiên, app server đang mặc định config là start:dev
// simple-portfolio/apps/server/package.json
{
"name": "server",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"dev": "nest start --watch", // change this
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
...
}
Sau đó ở root, hãy tạo 1 file package.json và thiết lập script cho phép khởi chạy các app cùng lúc.
// simple-portfolio/package.json
{
"name": "simple-portfolio",
"version": "1.0.0",
"description": "",
"scripts": {
"dev": "pnpm run --parallel dev"
},
"keywords": [],
"author": "hoaaq"
}
Cuối cùng thì chạy script và ta đã có 3 app lần lượt là server listen port 4000, office listen port 3500 và client listen port 3000.
# simple-portfolio
pnpm install && pnpm dev
apps/office dev: ▲ Next.js 14.2.5
apps/office dev: - Local: http://localhost:3500
apps/office dev: ✓ Starting...
apps/client dev: ▲ Next.js 14.2.5
apps/client dev: - Local: http://localhost:3000
apps/client dev: ✓ Starting...
apps/server dev: [11:20:33 PM] Found 0 errors. Watching for file changes.
apps/server dev: [Nest] 3838448 - 07/26/2024, 11:20:34 PM LOG [NestFactory] Starting Nest application...
apps/server dev: [Nest] 3838448 - 07/26/2024, 11:20:34 PM LOG [InstanceLoader] AppModule dependencies initialized +6ms
apps/server dev: [Nest] 3838448 - 07/26/2024, 11:20:34 PM LOG [RoutesResolver] AppController {/}: +4ms
apps/server dev: [Nest] 3838448 - 07/26/2024, 11:20:34 PM LOG [RouterExplorer] Mapped {/, GET} route +1ms
apps/server dev: [Nest] 3838448 - 07/26/2024, 11:20:34 PM LOG [NestApplication] Nest application successfully started +1ms
apps/office dev: ✓ Ready in 2s
apps/client dev: ✓ Ready in 2s
Qua bài viết này, chúng ta đã cùng nhau thiết lập một dự án web hoàn chỉnh với NestJS, NextJS và PNPM workspace. Bằng cách sử dụng monorepo, chúng ta có thể dễ dàng quản lý và chia sẻ code giữa các ứng dụng front-end và back-end.
Việc cấu hình và kết nối các ứng dụng giúp chúng ta có một hệ thống đồng bộ và hiệu quả hơn. Hy vọng rằng hướng dẫn này sẽ giúp bạn có một khởi đầu thuận lợi cho các dự án web của mình. Chúc bạn thành công!
Subscribe to my newsletter
Read articles from Âu Quốc Hòa directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by