Using API Extractor for TypeScript Libraries
AI generated
<T>
type
TypeScript · API Extractor · CI/CD · Documentation
Using API Extractor for TypeScript Libraries
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.

17 min read api-extractor.json · API report · release tags · API Documenter TypeScript 5.x · CI/CD

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.

11. FAQ: API Extractor for TypeScript Libraries

1What is the rollup for?
Consolidates many generated .d.ts files into one public declaration without exposing internal details.
2What happens on a report mismatch?
The CI build fails, the file must be updated locally and deliberately included in the pull request.
3What does ae-missing-release-tag mean?
An exported symbol has no release tag, so API Extractor cannot determine its consumer facing status.
4Difference between @beta and @public?
@public is stable with full guarantees, @beta may still change without a major release.
5When is ae-forgotten-export a problem?
When a public symbol references a non-exported internal type, which then cannot be imported.
6How to generate docs from the doc model?
With api-documenter markdown, which automatically generates Markdown files from the JSON doc model.
7Why never --local in CI?
Because it auto-updates the report file, letting API changes slip through unnoticed.
8Worth it for small libraries?
Less so, but the one-time configuration is cheap enough to pay off from medium size onward.
9Can it manage multiple entry points?
Yes, with multiple api-extractor.json files each with its own mainEntryPointFilePath.
10Does it replace unit tests?
No, it complements them and only checks the public type surface, not runtime behavior.