Skill v1.0.1
currentAutomated scan100/100+1 new, ~1 modified
version: "1.0.1" name: nestjs-doctor description: Scan a NestJS project with nestjs-doctor, present a health report, and fix issues interactively disable-model-invocation: true allowed-tools: Bash, Read, Edit, Glob, Grep, Write
/nestjs-doctor — NestJS Health Scanner & Fixer
v0.4.32
Scan the NestJS codebase, present a prioritized health report, and offer to fix every issue found.
Step 1: Scan
Run nestjs-doctor and capture the full JSON output:
npx nestjs-doctor $ARGUMENTS --json 2>/dev/null
Step 2: Summarize
Parse the JSON output above. Present a summary:
- Score: value / 100 (label)
- Severity counts: errors, warnings, info
- Project info: name, NestJS version, ORM, file count, module count
Step 3: Report
Group diagnostics by severity (errors first, then warnings, then info). Within each severity, group by category (security > correctness > architecture > performance).
For each group show:
- Rule name and severity
- Message and help text
- Number of occurrences
- Affected file paths (with line numbers)
If there are more than 30 diagnostics, show the top 30 (prioritizing errors and warnings) and ask if the user wants to see the rest.
Step 4: Offer to Fix
Ask the developer what they want to fix:
- Fix all — apply fixes for every diagnostic
- Fix by category — fix all issues in a specific category (security, correctness, architecture, performance)
- Fix specific rules — fix only specific rule violations
- Review only — no changes, just the report
Step 5: Fix
For each diagnostic to fix:
- Read the affected file
- Apply the appropriate fix based on the rule (see Fix Guide below)
- Explain what was changed and why
Fix Guide by Rule
Security
- no-hardcoded-secrets: Extract the secret value to an environment variable. Import
ConfigService, inject it, and usethis.configService.get('ENV_VAR_NAME'). Add the env var name to.env.exampleif it exists. - no-eval: Remove
eval()ornew Function(). Replace with safe alternatives:JSON.parse()for JSON, a proper expression parser for dynamic evaluation, or refactor to avoid dynamic code execution entirely. - no-csrf-disabled: Remove the code that explicitly disables CSRF protection, or add a comment explaining why it's intentionally disabled with a
// nestjs-doctor-ignorecomment. - no-dangerous-redirects: Validate redirect URLs against an allowlist of trusted domains. Never pass user input directly to
res.redirect(). - no-weak-crypto: Replace
createHash('md5')orcreateHash('sha1')withcreateHash('sha256')or stronger. - no-exposed-env-vars: Replace direct
process.env.Xaccess withConfigService. InjectConfigServiceand usethis.configService.get('X')orthis.configService.getOrThrow('X'). - no-exposed-stack-trace: Remove
error.stackfrom response objects. Log the stack trace server-side withthis.logger.error(error.stack)and return a generic error message to the client. - no-synchronize-in-production: Set
synchronize: falsein TypeORM config. Use migrations (typeorm migration:generateandtypeorm migration:run) for production schema changes. If needed only for development, guard withsynchronize: process.env.NODE_ENV !== 'production'. - no-raw-entity-in-response: Create a DTO class for the response and map entity fields to it. Alternatively, use
class-transformer's@Exclude()decorator on sensitive entity fields and@SerializeOptions()on the controller. Never return raw ORM entities from controller methods.
Correctness
- no-missing-injectable: Add
@Injectable()decorator to the class. Import it from@nestjs/common. - no-duplicate-routes: Remove or rename the duplicate route. Change the HTTP method or path to make each route unique.
- no-missing-guard-method: Add the
canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean>method to the guard class. ImplementCanActivateinterface. - no-missing-pipe-method: Add the
transform(value: any, metadata: ArgumentMetadata)method to the pipe class. ImplementPipeTransforminterface. - no-missing-filter-catch: Add the
catch(exception: T, host: ArgumentHost)method to the exception filter class. ImplementExceptionFilterinterface. - no-missing-interceptor-method: Add the
intercept(context: ExecutionContext, next: CallHandler): Observable<any>method. ImplementNestInterceptorinterface. - require-inject-decorator: Add
@Inject('TOKEN_NAME')decorator to untyped constructor parameters, or add a proper type annotation. - prefer-readonly-injection: Add the
readonlymodifier to constructor-injected parameters:constructor(private readonly myService: MyService). - require-lifecycle-interface: Add the corresponding interface to the class implements clause. E.g., if using
onModuleInit(), addimplements OnModuleInit. - no-empty-handlers: Add implementation to the empty HTTP handler. If it's a placeholder, add a
throw new NotImplementedException(). - no-async-without-await: If the message says "returns a Promise directly", remove the
asynckeyword since anew Promise()is already being constructed manually. Otherwise, either add anawaitexpression or remove theasynckeyword. - no-duplicate-module-metadata: Remove duplicate entries from
@Module()arrays (providers, controllers, imports, exports). - no-missing-module-decorator: Add
@Module({})decorator to the class. Import it from@nestjs/common. - no-fire-and-forget-async: Add
awaitbefore the async call to properly handle rejections. If fire-and-forget is intentional, prefix withvoidand add a.catch()handler:void this.service.sendEmail().catch(err => this.logger.error(err)).
Architecture
- no-business-logic-in-controllers: Extract the business logic (loops, complex conditionals, data transforms) into a service method. The controller should only call the service and return the result.
- no-repository-in-controllers: Remove the repository injection from the controller. Create or use an existing service that wraps the repository, and inject that service instead.
- no-orm-in-controllers: Remove PrismaService/EntityManager/DataSource injection from the controller. Create or use an existing service that wraps the ORM operations.
- no-circular-module-deps: Break the circular dependency. Extract shared functionality into a separate module, use
forwardRef(() => Module), or restructure the module boundaries. - no-manual-instantiation: Replace
new SomeService()with constructor injection. Add the service to the module's providers and inject it via the constructor. - no-service-locator: Replace
this.moduleRef.get(SomeService)with constructor injection: addprivate readonly someService: SomeServiceto the constructor. If the service is truly dynamic or lazily resolved, document the reason with a comment. - no-orm-in-services: Consider introducing a repository layer. Create a repository class that wraps ORM operations, and inject the repository into the service instead of the ORM directly.
- prefer-constructor-injection: Replace
@Inject()property injection with constructor injection. Move the dependency to a constructor parameter. - require-module-boundaries: Replace deep imports (
import { X } from '../other-module/services/x.service') with imports through the module's public API (barrel file / index.ts). - no-barrel-export-internals: Remove repository re-exports from barrel files. Repositories should only be accessible within their own module.
Performance
- no-sync-io: Replace synchronous I/O (
readFileSync,writeFileSync, etc.) with async equivalents (readFile,writeFilefromfs/promises). - no-blocking-constructor: Move async operations and loops out of the constructor into
onModuleInit()lifecycle hook. ImplementOnModuleInitinterface. - no-dynamic-require: Replace
require(variable)with a static import or a switch/map pattern that uses staticrequire()calls. - no-unused-providers: Remove the unused provider from the module's providers array, or start using it. Providers with self-activating decorators (@Cron, @OnEvent, @Process) are automatically excluded. If it's intended for external consumers, add it to the module's exports.
- no-request-scope-abuse: Remove
Scope.REQUESTunless the provider genuinely needs per-request state (e.g., request-scoped context likeREQUESTobject). UseScope.DEFAULT(singleton) orScope.TRANSIENTinstead. Remember that request scope propagates to all dependents. - no-unused-module-exports: Remove the unused export from the module's exports array, or start importing the module where the exported provider is needed.
- no-orphan-modules: Import this module in another module that needs it, or remove it if it's truly unused. If it's the root module, this can be ignored.
Step 6: Verify
After applying fixes, suggest re-running the scan:
Fixes applied. Run/nestjs-doctoragain to verify the score improved.