@microsoft/api-extractor
@microsoft/api-extractor is a build-time analyzer for the exported surface of a TypeScript library. Starting from an emitted declaration entry point, it can create a reviewable .api.md contract, flag missing exports and inconsistent @public, @beta, @alpha, or @internal boundaries, roll a declaration tree into one or more .d.ts files, and emit an .api.json model for documentation tools. It is not a TypeScript compiler, JavaScript bundler, test runner, or finished documentation renderer.
Worth the ceremony for public TypeScript libraries whose API stability is a product promise. Skip it for applications and small internal packages that only need a declaration bundle, because the report workflow and release-tag discipline are the point, not incidental features.
Use it if
- You publish a TypeScript library and want pull requests to show an explicit, version-controlled API contract diff
- You need one rolled-up declaration file or separate public, beta, and alpha type surfaces
- You use TSDoc release tags and want errors when public APIs expose less-stable or forgotten types
- You need a structured API model that @microsoft/api-documenter or a custom documentation pipeline can consume
- You are building an application rather than a reusable TypeScript package: API Extractor analyzes a package entry point and adds no value to private app code
- You only need to concatenate declaration files: rollup-plugin-dts or dts-bundle-generator has much less configuration and does not impose API reports or TSDoc release policy
- Your package has many independent public entry points: the required mainEntryPointFilePath starts analysis from one declaration module, and multi-entry packages need deliberate facade or per-entry strategies
- You cannot align its embedded TypeScript 5.9.3 engine with your emitted declarations: the package ships its own compiler and documents an alternate compiler-folder escape hatch for mismatched system typings
- You want a light dev dependency: version 7.58.12 brings TypeScript plus twelve other direct dependencies and generates reports, temp files, rollups, metadata, and optional doc models that the team must understand and review
Setup reality
Install @microsoft/api-extractor as a dev dependency and run npx api-extractor init to create api-extractor.json. It cannot analyze .ts source directly: first configure TypeScript to emit declarations and make mainEntryPointFilePath point at the built .d.ts, .d.mts, or other declaration entry. This makes task order important in local builds and CI; stale or missing declarations produce misleading failures. The config is JSON with comments and required enabled switches for apiReport, docModel, and dtsRollup. Defaults assume tsconfig.json in the project folder, an etc directory for the committed report, temp for the comparison report and API model, dist for the untrimmed declaration rollup, and CRLF output unless newlineKind is changed to lf or os. The first local run with --local creates or updates the approved .api.md file; review and commit that file. A production run without --local compares generated output with the approved report and fails on changes. Warnings also fail a production invocation by default, while local mode ignores warnings for the succeeded result. Source declarations need TSDoc comments and usually @public, @beta, @alpha, or @internal tags; forgotten exports and incompatible release tags are written into the API report by default. The tool owns neither JavaScript bundling nor docs HTML. api-documenter consumes .api.json, and your package.json types field must point to whichever rolled-up .d.ts you publish. Monorepos usually share config through extends or a Rush rig. When project and bundled TypeScript system declarations disagree, prefer fixing the mismatch before using skipLibCheck or --typescript-compiler-folder, because the template warns that skipped checks can produce incomplete output.
Patterns
Create the configuration templateinitialize-config
npm install --save-dev @microsoft/api-extractor
npx api-extractor initThe generated api-extractor.json is intentionally verbose and uses JSON-with-comments; trim it only after understanding inherited defaults.
Prepare TypeScript declarations for analysisemit-declarations
{
"compilerOptions": {
"declaration": true,
"declarationMap": true,
"emitDeclarationOnly": true,
"outDir": "dist/types"
},
"include": ["src"]
}API Extractor starts from emitted declarations, not .ts source. Run tsc before the extractor and avoid analyzing stale output.
Configure reports and a declaration rollupconfigure-minimal-run
{
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
"mainEntryPointFilePath": "<projectFolder>/dist/types/index.d.ts",
"newlineKind": "lf",
"apiReport": { "enabled": true },
"docModel": { "enabled": false },
"dtsRollup": {
"enabled": true,
"untrimmedFilePath": "<projectFolder>/dist/index.d.ts"
}
}apiReport, docModel, and dtsRollup each require an explicit enabled value; the entry path must already exist when the run starts.
Update the API report during developmentapprove-local-report
npx api-extractor run --local --verboseLocal mode creates or updates the approved report under etc by default. Review that diff before committing it as the new contract.
Enforce the approved report in CIcheck-api-in-ci
npm run build:types
npx api-extractor runWithout --local, a changed .api.md report or warnings routed at the default warning level cause a failed production result.
Mark public and beta exports with TSDoctag-api-release-level
/** @public */
export interface ClientOptions {
endpoint: string;
}
/** @beta */
export function createExperimentalClient(options: ClientOptions): Client;A public declaration must not expose beta or internal types; API Extractor reports incompatible release-tag boundaries.
Add package-level TSDocdocument-package-entry
/**
* Typed client utilities for the Example API.
*
* @packageDocumentation
*/
export { Client } from './Client.js';Place @packageDocumentation in the declaration entry module; malformed TSDoc is routed through tsdoc message settings.
Review public and beta surfaces separatelygenerate-report-variants
{
"apiReport": {
"enabled": true,
"reportVariants": ["public", "beta"]
}
}The public variant contains @public items; beta contains @public plus @beta. File names receive matching variant suffixes.
Publish separate public and beta declarationsemit-trimmed-rollups
{
"dtsRollup": {
"enabled": true,
"untrimmedFilePath": "",
"publicTrimmedFilePath": "<projectFolder>/dist/index.d.ts",
"betaTrimmedFilePath": "<projectFolder>/dist/index-beta.d.ts",
"omitTrimmingComments": true
}
}Update package.json types and exports to point at the intended rollup; generating files alone does not change what consumers receive.
Generate an API model for documentationemit-documentation-model
{
"docModel": {
"enabled": true,
"apiJsonFilePath": "<projectFolder>/temp/<unscopedPackageName>.api.json",
"projectFolderUrl": "https://github.com/example/widgets/tree/main"
}
}The .api.json file is an intermediate model. Use @microsoft/api-documenter or custom code to render reader-facing pages.
Make forgotten exports a build errorroute-specific-message
{
"messages": {
"extractorMessageReporting": {
"ae-forgotten-export": {
"logLevel": "error",
"addToApiReportFile": true
}
}
}
}A forgotten export is a type reachable from the public surface but not exported by name; fixing the export is usually better than hiding the message.
Run API Extractor from a Node build scriptinvoke-programmatically
import path from 'node:path';
import { Extractor } from '@microsoft/api-extractor';
const result = Extractor.loadConfigAndInvoke(
path.resolve('api-extractor.json'),
{ localBuild: false, printApiReportDiff: true },
);
if (!result.succeeded) process.exitCode = 1;The result exposes errorCount, warningCount, apiReportChanged, and succeeded; localBuild changes whether warnings fail the result and updates approved reports.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| rollup-plugin-dts | npm | Choose it when declaration bundling inside an existing Rollup build is the only requirement |
| dts-bundle-generator | npm | Choose it for a focused CLI that emits bundled declarations without API review files or release tags |
| typedoc | npm | Choose it when the primary deliverable is browsable TypeScript API documentation rather than contract review and declaration rollups |