from many .d.ts files to one stable public API
A TypeScript library with dozens of internal modules produces just as many individual type declaration files on a standard build, with no visibility into which of them actually belong to the public API. API Extractor solves exactly this problem: a single rollup, a machine readable API report acting as a contract test, and release tags that spell out exactly what consumers are allowed to use.
Table of Contents
- 1. The problem: many individual .d.ts files instead of a stable API
- 2. What API Extractor does: rollup, API report, doc model
- 3. Installation and configuring api-extractor.json
- 4. The API report as a contract test in CI
- 5. Warnings: missing release tags and unintended exports
- 6. Release tags: public, beta, alpha, internal
- 7. Integration with API Documenter for Markdown docs
- 8. CI pipeline: run --local vs. CI mode
- 9. API Extractor compared to manual alternatives
- 10. Summary
- 11. FAQ
1. The problem: many individual .d.ts files instead of a stable API
The TypeScript compiler produces a separate .d.ts file for every source file. For a small TypeScript library with a handful of files that is no problem, but for a grown library with fifty or a hundred internal modules it creates a confusing web of type declaration files importing each other. Neither consumers nor the team itself can clearly see which of these many files actually belong to the public API and which are pure internal implementation details.
Without a tool such as API Extractor, the only safeguard against accidentally exported internal details is a manual code review that would need to be carried out fully on every change. That does not scale for a growing TypeScript library, and this is exactly where API Extractor comes in: it makes the actual public API visible, checkable and machine enforceable, instead of silently leaving it to individual developers' discipline.
2. What API Extractor does: rollup, API report, doc model
API Extractor, an open source tool from Microsoft's Rush Stack project, fulfills three related tasks for a TypeScript library. First, it produces a single rolled up declaration file from the many individual compiler generated .d.ts files, containing only the actually exported symbols without exposing internal implementation details. Second, it produces the API report already mentioned, a compact, version controlled text file with the complete public signature.
Third, API Extractor optionally produces a so called API doc model, a machine readable JSON representation of the entire public API including all JSDoc comments. This doc model is the foundation for automatically generated documentation with the sibling project API Documenter, which produces readable Markdown pages from the doc model for every exported class, function and type.
3. Installation and configuring api-extractor.json
Installation happens via npm install --save-dev @microsoft/api-extractor, followed by a configuration file api-extractor.json in the project root. The most important setting is mainEntryPointFilePath, the path to the generated .d.ts file of the main entry point, typically dist/index.d.ts. API Extractor reads this file and follows every import referenced within it to determine the complete public API.
The configuration additionally defines where the rollup and the API report get written, as well as whether and how the doc model is produced. A typical configuration for a TypeScript library enables both docModel and apiReport, while tsdocMetadata usually stays disabled unless separate TSDoc tooling integration is planned.
{
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
"mainEntryPointFilePath": "<projectFolder>/dist/index.d.ts",
"apiReport": {
"enabled": true,
"reportFolder": "<projectFolder>/etc/",
"reportTempFolder": "<projectFolder>/temp/"
},
"docModel": {
"enabled": true,
"apiJsonFilePath": "<projectFolder>/temp/<unscopedPackageName>.api.json"
},
"dtsRollup": {
"enabled": true,
"untrimmedFilePath": "<projectFolder>/dist/<unscopedPackageName>.d.ts"
},
"messages": {
"extractorMessageReporting": {
"ae-missing-release-tag": { "logLevel": "warning" }
}
}
}
4. The API report as a contract test in CI
The API report is API Extractor's central innovation for backwards compatibility: an .api.md file with the complete public signature of the TypeScript library in a stable, diff friendly text format. This file gets checked into the repository like any other source file. On every build, API Extractor compares the freshly generated report against the checked in version and reports a mismatch as an error if the two differ.
This mechanism effectively turns the API report into a contract test: every change to the public API must become explicitly visible in the pull request, because the .api.md file changes alongside it and shows up in the reviewer's diff. A reviewer sees at a glance whether a supposedly internal refactor accidentally touched the public signature, without needing to manually search through the entire codebase.
# etc/query-builder.api.md — checked into version control
## API Report File for "@mironsoft/query-builder"
```ts
export class QueryBuilder {
constructor(options: QueryBuilderOptions);
// (undocumented)
limit(count: number): this;
where(conditions: WhereClause[]): this;
}
export interface QueryBuilderOptions {
dialect: "mysql" | "postgres";
timeoutMs?: number;
}
```
5. Warnings: missing release tags and unintended exports
API Extractor generates a set of standardized warnings that surface typical API design mistakes in a TypeScript library. The most common is ae-missing-release-tag, which indicates that an exported symbol carries no release tag such as @public or @internal. Without this tag, API Extractor cannot automatically decide whether the symbol is actually meant for consumers.
A second important warning is ae-forgotten-export, which occurs when a publicly exported symbol internally uses a type that is not itself exported. The result for consumers would be a type that visibly shows up in the API but cannot be imported directly, a subtle and frustrating situation for consumers. This warning reliably catches exactly such half finished export situations before consumers report them.
6. Release tags: public, beta, alpha, internal
Release tags are special JSDoc tags that explicitly mark every exported symbol of a TypeScript library as @public, @beta, @alpha or @internal. @public marks stable API for which full backwards compatibility guarantees apply. @beta marks experimental but already usable API whose signature may still change without counting as a major-release-worthy break. @alpha is meant for early, unstable prereleases, @internal for symbols that need to be exported for technical reasons but must never be used by consumers.
The practical benefit shows up at rollup time: API Extractor can produce separate .d.ts files for different release stages, for example a complete variant for internal purposes and a cleaned up variant that contains only @public symbols and gets shipped to consumers. This way, the internal complexity of a TypeScript library stays entirely invisible to consumers.
/**
* Executes the built query against the configured connection.
* @public
*/
export class QueryBuilder {
/**
* Applies an experimental query hint. Signature may change
* before this reaches @public status.
* @beta
*/
withHint(hint: QueryHint): this {
// ...
return this;
}
}
/**
* @internal
*/
export function _normalizeDialectName(input: string): string {
return input.trim().toLowerCase();
}
7. Integration with API Documenter for Markdown docs
The API doc model produced in the previous step is the input for api-documenter, a separate command line tool that automatically generates Markdown files from it for every exported class, interface and function. These generated files contain signatures, JSDoc descriptions, parameter tables and links between related symbols, without a developer having to maintain the documentation by hand.
The decisive advantage over handwritten documentation: the generated documentation can never drift from the actual API, because it is produced directly from the compiled code. Changes to signatures or JSDoc comments automatically show up in the next generated docs, without anyone needing to remember to update a separate documentation file.
# Generate the API doc model first, then render Markdown from it
npx api-extractor run --local
npx api-documenter markdown --input-folder temp --output-folder docs/api
# docs/api now contains one .md file per exported symbol,
# e.g. docs/api/query-builder.querybuilder.where.md
8. CI pipeline: run --local vs. CI mode
API Extractor distinguishes two execution modes meant for different purposes. api-extractor run --local updates the API report directly on disk and is meant for local development, when a developer has deliberately made an API change and wants to update the report accordingly. The plain call api-extractor run without --local, on the other hand, only compares and fails with an error code as soon as a mismatch exists, without modifying the file.
Only the second mode belongs in the CI pipeline, because an automatically updated report file in CI would undermine the entire point of the contract test: every change would then need to be deliberately reviewed and confirmed locally by a human before it can even find its way into the pull request. This forces a deliberate engagement with every API change instead of letting it slip through unnoticed.
9. API Extractor compared to manual alternatives
Without a dedicated tool such as API Extractor, only manual or semi automated alternatives remain, which are noticeably less reliable. The table below compares the options.
| Approach | Detects breaking changes | Effort |
|---|---|---|
| Manual code review | Unreliable, human factor | High, repeated on every review |
| tsc --declaration alone | No, no comparison mechanism | Low, but incomplete |
| Custom diff script | Partial, depending on implementation | High, custom build |
| API Extractor | Yes, reliable and automated | Low, one-time configuration |
The one-time configuration effort for API Extractor pays for itself within a few pull requests, as soon as the first unintended API change gets caught early through the automated report comparison instead of surfacing as a consumer bug report weeks later.
# .github/workflows/api-contract.yml — compare-only mode, never --local
name: API Contract Check
on: [pull_request]
jobs:
api-report:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npm ci
- run: npm run build
- run: npx api-extractor run --verbose
# fails with a non-zero exit code if etc/*.api.md would change
Mironsoft
TypeScript libraries, API governance and automated documentation
Want API Extractor set up in your library?
We configure API Extractor, set up the API report as a contract test in your CI pipeline, and connect it to automatically generated documentation through API Documenter.
Configuration
Setting up api-extractor.json and a release tag strategy for your library
CI integration
Building the API report into existing pipelines as an automated contract test
Documentation
Setting up API Documenter for always current, automatically generated Markdown docs
10. Summary
API Extractor turns an unwieldy collection of generated .d.ts files into a single, controlled public API surface. The rollup consolidates all exported symbols, the API report makes every change to that surface visible as a diff in the pull request, and release tags such as @public, @beta and @internal give every exported symbol a clear status with corresponding backwards compatibility expectations.
Integration with API Documenter additionally produces always current Markdown documentation directly from the compiled code, without manual maintenance effort. In the CI pipeline, the pure comparison mode without --local ensures every API change gets deliberately reviewed before it reaches a release, instead of slipping in unnoticed.
API Extractor for TypeScript libraries — the essentials at a glance
Rollup
Consolidates many generated .d.ts files into one single, controlled public declaration.
API report
Checked in .api.md file acts as a contract test, any mismatch fails the CI build.
Release tags
@public, @beta, @alpha and @internal give every symbol clear backwards compatibility expectations.
Documentation
API Documenter produces always current Markdown docs directly from the doc model.