Node API

Types ship with the package. diagnose() returns the same result the CLI formats:

import { diagnose, diagnoseMonorepo } from "nestjs-doctor";
 
const result = await diagnose("./my-nestjs-app");
result.score; // { value: 82, label: "Good" }
result.diagnostics; // Diagnostic[]
result.summary; // { total, errors, warnings, info, byCategory }
 
const mono = await diagnoseMonorepo("./my-monorepo");
mono.isMonorepo; // true
mono.subProjects; // [{ name: "api", result }, ...]
mono.combined; // Merged DiagnoseResult

DiagnoseResult carries score, diagnostics, summary, project, ruleErrors, elapsedMs, and schema when an ORM was detected.

Incremental scanning

Editors and language servers prepare the context once, then re-scan single files as they change instead of calling diagnose() again:

import {
  prepareAnalysis,
  updateFile,
  checkFile,
  checkAllFiles,
  checkProject,
} from "nestjs-doctor";
 
const { context, customRuleWarnings } = await prepareAnalysis("./my-nestjs-app");
 
updateFile(context, "/absolute/path/to/changed-file.ts");
 
const { diagnostics, errors } = checkFile(context, "/absolute/path/to/changed-file.ts");
const allFileResults = checkAllFiles(context);
const projectResults = checkProject(context);

prepareAnalysis parses every file, builds the module graph, and resolves the providers. updateFile re-parses one file and refreshes the module graph, the providers, and the endpoint and schema graphs.

checkFile runs the file-scoped rules on that one file, and checkAllFiles runs them across every file. checkProject runs the project-scoped ones.

Granular updates

Refresh the module graph or the provider map on their own, without going through updateFile:

import { updateModuleGraphForFile, updateProvidersForFile } from "nestjs-doctor";
 
updateModuleGraphForFile(
  context.moduleGraph,
  context.astProject,
  filePath,
  context.pathAliases,
);
updateProvidersForFile(context.providers, context.astProject, filePath);

Both return void and mutate the object handed to them. That object is the one the context holds, so a later checkProject(context) reads the updated graph.

Pass context.pathAliases. It defaults to an empty map, and updateProvidersForFile has no such parameter. Omit it and the file's aliased imports stop resolving, so that module's edges vanish with no error.

getRules() is exported alongside them and returns every built-in rule as AnyRule[].

AnalysisContext

The prepareAnalysis function returns an AnalysisContext that holds:

FieldTypeDescription
astProjectProjectts-morph project with all source files
configNestjsDoctorConfigResolved configuration
filesstring[]Collected file paths
fileRulesRule[]File-scoped rules (filtered by config)
projectRulesProjectRule[]Project-scoped rules (filtered by config)
moduleGraphModuleGraphImport/export graph
endpointGraphEndpointGraphTraced HTTP routes and the dependencies each one pulls
providersMap<string, ProviderInfo>Resolved NestJS providers
guardDecoratorsGuardDecoratorIndexPer file, the names of the wrapper decorators that apply @UseGuards()
pathAliasesPathAliasMaptsconfig paths aliases, each mapped to its absolute targets
projectProjectInfoDetected project metadata (name, version, ORM, framework)
schemaGraphSchemaGraph | undefinedExtracted database schema (if ORM detected)
schemaRulesSchemaRule[]Schema-scoped rules (filtered by config)
targetPathstringRoot directory of the scanned project
installRootstring | undefinedWhere to resolve node_modules from, when that is not targetPath