Trong quá trình phát triển với NestJS, bạn có thể gặp nhiều lỗi khác nhau khi làm quen với framework.
Lỗi “Cannot resolve dependency”
Gợi ý Xem qua NestJS Devtools, công cụ này giúp bạn xử lý lỗi “Cannot resolve dependency” một cách dễ dàng.
Có lẽ đây là thông báo lỗi phổ biến nhất, xảy ra khi Nest không thể resolve được các dependency của một provider. Thông báo lỗi thường có dạng như sau:
Nest can't resolve dependencies of the <provider> (?). Please make sure that the argument <unknown_token> at index [<index>] is available in the <module> context.
Potential solutions:
- Is <module> a valid NestJS module?
- If <unknown_token> is a provider, is it part of the current <module>?
- If <unknown_token> is exported from a separate @Module, is that module imported within <module>?
@Module({
imports: [ /* the Module containing <unknown_token> */ ]
})
Nguyên nhân phổ biến nhất của lỗi này là chưa khai báo <provider> trong mảng providers của module. Hãy đảm bảo provider đó thực sự có trong mảng providers và tuân theo các quy tắc chuẩn khi khai báo provider trong NestJS.
Có một vài “bẫy” thường gặp. Một trong số đó là đặt provider vào mảng imports. Nếu rơi vào trường hợp này, lỗi sẽ hiển thị tên của provider ở vị trí đáng lẽ phải là <module>.
Nếu gặp lỗi này khi đang phát triển, hãy xem module được nhắc tới trong thông báo lỗi và kiểm tra mảng providers của nó. Với mỗi provider trong mảng providers, đảm bảo module đó có quyền truy cập tới toàn bộ dependency cần thiết. Rất thường xuyên, providers bị khai báo trùng lặp ở cả “Feature Module” và “Root Module”, khiến Nest cố gắng khởi tạo provider đó hai lần. Nhiều khả năng, module chứa <provider> đang bị trùng lặp nên được thêm vào mảng imports của “Root Module” thay vì khai báo lại provider.
Nếu <unknown_token> ở trên là dependency, có thể bạn đang gặp circular file import (import vòng giữa các file). Đây là lỗi khác với circular dependency error bên dưới, vì thay vì các provider phụ thuộc lẫn nhau qua constructor, đây chỉ đơn giản là hai file import lẫn nhau. Một trường hợp thường gặp là file module khai báo một token rồi import một provider, còn provider đó lại import hằng số token từ chính file module. Nếu bạn dùng barrel file, hãy đảm bảo các import trong barrel không vô tình tạo ra kiểu import vòng như vậy.
Nếu <unknown_token> ở trên là Object, nghĩa là bạn đang inject bằng một type/interface mà không có token provider hợp lệ. Để khắc phục, hãy đảm bảo:
- bạn đang import class reference, hoặc dùng custom token với decorator
@Inject(). Đọc thêm ở trang custom providers, và - với các provider dạng class, bạn đang import class cụ thể (concrete class) thay vì chỉ import type qua cú pháp
import type ....
Ngoài ra, hãy đảm bảo bạn không vô tình inject provider vào chính nó, vì self-injection không được NestJS cho phép. Khi điều này xảy ra, <unknown_token> thường sẽ bằng đúng <provider>.
Nếu bạn đang làm việc trong một monorepo, bạn có thể gặp lỗi tương tự như trên nhưng với core provider tên là ModuleRef đóng vai trò <unknown_token>:
Nest can't resolve dependencies of the <provider> (?).
Please make sure that the argument ModuleRef at index [<index>] is available in the <module> context.
...
Trường hợp này thường xảy ra khi project của bạn load hai bản Node module của package @nestjs/core, ví dụ như sau:
.
├── package.json
├── apps
│ └── api
│ └── node_modules
│ └── @nestjs/bull
│ └── node_modules
│ └── @nestjs/core
└── node_modules
├── (other packages)
└── @nestjs/core
Giải pháp:
- Với Yarn Workspaces, dùng tính năng nohoist để ngăn package
@nestjs/corebị hoist. - Với pnpm Workspaces, khai báo
@nestjs/corelà peerDependencies trong module khác của bạn, và thêm"dependenciesMeta": {"other-module-name": {"injected": true }}trong package.json của app nơi module đó được import. Xem: dependenciesmetainjected
Ghi chú người dịch Lỗi “hai bản @nestjs/core” này giống hệt vấn đề “duplicate React” trong monorepo React (hai instance của React khiến hook báo lỗi vô lý), hoặc vendor bị nhân đôi trong Go workspace - nguyên nhân gốc luôn là package manager hoist/cài trùng cùng một thư viện ở hai node_modules khác nhau, khiến runtime coi hai bản đó là hai “class” khác nhau dù cùng tên. DI container của Nest so khớp theo token/class reference, nên hai bản @nestjs/core sẽ có hai class ModuleRef khác nhau về mặt tham chiếu, dẫn đến việc resolve thất bại dù code nhìn qua hoàn toàn đúng.
Lỗi “Circular dependency”
Đôi khi bạn sẽ thấy khó tránh khỏi circular dependency (phụ thuộc vòng) trong ứng dụng của mình. Bạn sẽ cần thực hiện một số bước để giúp Nest resolve được các phụ thuộc này. Lỗi phát sinh từ circular dependency thường trông như sau:
Nest cannot create the <module> instance.
The module at index [<index>] of the <module> "imports" array is undefined.
Potential causes:
- A circular dependency between modules. Use forwardRef() to avoid it. Read more: https://docs.nestjs.com/fundamentals/circular-dependency
- The module at index [<index>] is of type "undefined". Check your import statements and the type of the module.
Scope [<module_import_chain>]
# example chain AppModule -> FooModule
Circular dependency có thể phát sinh từ việc các provider phụ thuộc lẫn nhau, hoặc từ việc các file TypeScript phụ thuộc lẫn nhau để lấy hằng số, chẳng hạn như export hằng số từ một file module rồi import chúng trong một file service. Với trường hợp sau, nên tạo một file riêng để chứa các hằng số. Với trường hợp trước, hãy làm theo hướng dẫn về circular dependency và đảm bảo cả module lẫn provider đều được đánh dấu bằng forwardRef.
Debug lỗi dependency
Bên cạnh việc tự tay kiểm tra dependency có đúng hay không, kể từ Nest 8.1.0, bạn có thể set biến môi trường NEST_DEBUG thành một chuỗi mang giá trị truthy, để có thêm log chi tiết trong lúc Nest resolve toàn bộ dependency của ứng dụng.

Trong hình trên, chuỗi màu vàng là host class của dependency đang được inject, chuỗi màu xanh dương là tên của dependency được inject, hoặc injection token của nó, và chuỗi màu tím là module nơi dependency đang được tìm kiếm. Dựa vào đây, bạn thường có thể lần theo quá trình resolve dependency để hiểu chuyện gì đang xảy ra và vì sao gặp vấn đề với dependency injection.
Ghi chú người dịch Cơ chế này tương tự việc bật DEBUG=express:* trong Express: phơi bày phần framework làm ngầm. Nếu từng dùng google/wire ở Go, bạn sẽ thấy khác biệt: wire giải quyết dependency lúc sinh code (lỗi thiếu provider báo ngay khi build), còn Nest giải quyết lúc runtime khi app khởi động — nên mới cần log debug như thế này. Điểm khác là ở Nest, việc “tìm dependency ở module nào” phụ thuộc vào cây import giữa các module (imports/exports), nên log màu tím ở trên chính là manh mối quan trọng nhất khi bạn nghi ngờ mình quên exports một provider.
“File change detected” lặp vô tận
Người dùng Windows dùng TypeScript phiên bản 4.9 trở lên có thể gặp phải vấn đề này.
Điều này xảy ra khi bạn chạy ứng dụng ở watch mode, ví dụ npm run start:dev, và thấy một vòng lặp vô tận các dòng log:
XX:XX:XX AM - File change detected. Starting incremental compilation...
XX:XX:XX AM - Found 0 errors. Watching for file changes.
Khi bạn dùng NestJS CLI để chạy ứng dụng ở watch mode, việc này được thực hiện bằng cách gọi tsc --watch, và kể từ phiên bản 4.9 của TypeScript, một chiến lược mới để phát hiện thay đổi file được sử dụng, và đây nhiều khả năng chính là nguyên nhân gây ra vấn đề này.
Để khắc phục, bạn cần thêm một cấu hình vào file tsconfig.json, ngay sau option "compilerOptions", như sau:
"watchOptions": {
"watchFile": "fixedPollingInterval"
}
Cấu hình này bảo TypeScript dùng phương pháp polling để kiểm tra thay đổi file thay vì dùng file system event (phương pháp mặc định mới), vì cách mới này có thể gây vấn đề trên một số máy.
Bạn có thể đọc thêm về option "watchFile" trong tài liệu TypeScript.