The Problem
When working with a NestJS application in a Turborepo monorepo, you might encounter this frustrating error:
Cannot find module '@package/nestjs-health' or its corresponding type declarations
This error occurs even when your imports and directory structure seem correct. After much investigation, the solution turned out to be much simpler than expected: properly configuring your package.json.
The Solution: Key Package.json Fields
The key to solving this issue lies in adding specific fields to your package.json. Here’s the critical configuration that fixed the problem:
{
"name": "@package/nestjs-health",
"version": "1.0.0",
"main": "dist/index.js", // 👈 Critical field
"types": "dist/index.d.ts", // 👈 Critical field
"private": true,
"scripts": {
"build": "tsc", // 👈 Important script
"dev": "tsc --watch" // 👈 Important script
}
}
Let’s break down why each of these fields is important:
1. Main Field
"main": "dist/index.js"
- Tells Node.js where to find the entry point of your package
- Must point to the compiled JavaScript file, not the TypeScript source
- The
dist
directory is where TypeScript compiler outputs the compiled files
2. Types Field
"types": "dist/index.d.ts"
- Tells TypeScript where to find type declarations
- Essential for proper TypeScript integration
- Must point to the generated .d.ts file
3. Build Scripts
"scripts": {
"build": "tsc",
"dev": "tsc --watch"
}
build
: Compiles TypeScript filesdev
: Watches for changes and recompiles automatically- Essential for development workflow
Why This Works
- Proper Module Resolution
- The
main
field ensures Node.js can find your compiled code - Without this, Node tries to use the source TypeScript files directly
- The
- Type Support
- The
types
field enables TypeScript to find type declarations - Prevents “cannot find module” type errors
- The
- Development Workflow
- Build scripts ensure your code is properly compiled
- Watch mode keeps compiled files in sync with source changes
Complete Working Configuration
Here’s the full package.json that solved the issue:
{
"name": "@package/nestjs-health",
"version": "1.0.0",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"private": true,
"scripts": {
"build": "tsc",
"dev": "tsc --watch",
"format": "bun run format:prettier && bun run format:es",
"format:prettier": "prettier --write .",
"format:es": "eslint . --fix",
"lint": "bun run lint:prettier && bun run lint:typescript && bun run lint:es",
"lint:typescript": "tsc --noEmit",
"lint:prettier": "prettier --check .",
"lint:es": "eslint .",
"precommit:format": "prettier --write --ignore-unknown"
},
"dependencies": {
"@nestjs/common": "^10.0.5"
},
"devDependencies": {
"@tooling/typescript": "*",
"@tooling/prettier": "*"
}
}
Implementation Steps
- Update Package.json
cd packages/nestjs-health
# Update package.json with the new fields
2. Build the Package
bun run build
or
npm run build
3. Verify Directory Structure
packages/nestjs-health/
├── dist/
│ ├── index.js
│ ├── index.d.ts
│ └── ...
├── src/
│ └── index.ts
└── package.json
Common Gotchas
- Missing dist Directory
- Ensure TypeScript is actually compiling your files
- Check your tsconfig.json has correct
outDir
configuration
- Incorrect Paths
- Double-check the paths in
main
andtypes
fields - Make sure they match your actual file structure
- Double-check the paths in
- Build Order
- Ensure your Turborepo pipeline builds packages before apps
- Add proper dependencies in your turbo.json
Testing the Fix
After applying these changes, you should be able to:
- Import your package in any app within your monorepo
import { HealthModule } from '@package/nestjs-health';
- Get proper TypeScript support and auto-completion
- Have working builds without module resolution errors
Conclusion
While module resolution issues in a Turborepo + NestJS setup can be frustrating, the solution often lies in proper package.json configuration. The key fields (main
, types
) and proper build scripts ensure that both JavaScript runtime and TypeScript compiler can find and use your code correctly.
Remember: When working with TypeScript packages in a monorepo, always ensure your package.json properly points to the compiled output, not the source files!
Quick Reference
{
"main": "dist/index.js", // Points to compiled JS
"types": "dist/index.d.ts", // Points to type declarations
"scripts": {
"build": "tsc", // Compiles TypeScript
"dev": "tsc --watch" // Development mode
}
}
This configuration has proven to be the key to solving module resolution issues in NestJS + Turborepo setups.