Breaking Changes

Detect breaking API changes between versions using oasdiff.

We recommend using oasdiff to detect breaking changes in your API. oasdiff compares OpenAPI specs and reports breaking changes, changelogs, and diffs. Since ts-kizuna generates OpenAPI specs from your routes, you can wire up oasdiff to catch breaking changes before they ship.

Create a script that prints your OpenAPI spec as YAML to stdout. This is the interface oasdiff and CI workflows consume.

scripts/print-openapi.ts
import { generateOpenApi } from '@ts-kizuna/openapi';
import { contract } from '../src/contract';

const spec = generateOpenApi(contract);

console.log(spec('yaml'));

Add a script to your package.json:

package.json
{
    "scripts": {
        "print:openapi": "tsx scripts/print-openapi.ts"
    }
}

GitHub Actions

Add a workflow that runs oasdiff on every pull request. This generates the OpenAPI spec from both the base branch and the PR branch, then checks for breaking changes.

.github/workflows/breaking-changes.yml
# .github/workflows/breaking-changes.yml
name: Breaking Changes

on:
    pull_request:
        branches: [main]

jobs:
    breaking-changes:
        runs-on: ubuntu-latest
        steps:
            - name: Checkout PR branch
              uses: actions/checkout@v4
              with:
                  ref: ${{ github.head_ref }}

            - name: Setup
              uses: pnpm/action-setup@v4
            - uses: actions/setup-node@v4
              with:
                  node-version: 22
                  cache: pnpm

            - name: Install and build (PR)
              run: |
                  pnpm install --frozen-lockfile
                  pnpm build
            - name: Generate PR spec
              run: pnpm print:openapi > pr-api.yaml

            - name: Checkout base branch
              uses: actions/checkout@v4
              with:
                  ref: ${{ github.base_ref }}
                  clean: false
                  path: base

            - name: Install and build (base)
              working-directory: base
              run: |
                  pnpm install --frozen-lockfile
                  pnpm build
            - name: Generate base spec
              working-directory: base
              run: pnpm print:openapi > ../base-api.yaml

            - name: Check for breaking changes
              id: oasdiff
              uses: oasdiff/oasdiff-action/breaking@main
              with:
                  base: base-api.yaml
                  revision: pr-api.yaml
                  fail-on: ERR

            - name: Require label for breaking changes
              if: steps.oasdiff.outcome == 'failure'
              run: |
                  if [[ "${{ contains(github.event.pull_request.labels.*.name, 'breaking changes') }}" == "true" ]]; then
                      echo "Breaking changes acknowledged via label."
                  else
                      echo "::error::Breaking changes detected. Add the 'breaking changes' label to acknowledge."
                      exit 1
                  fi

If oasdiff detects breaking changes, the PR is blocked until someone adds the breaking changes label, forcing explicit acknowledgment from the team.

On this page