# An API-First Workflow in CI/CD — OpenAPI / Swagger

Source: https://www.skillbyai.com/en/openapi/p-workflow

> Automate validation, breaking-change checks, docs and SDKs in a pipeline.

## The contract as a build artefact

A mature API workflow treats the OpenAPI document like code. It lives in **Git** (in the service repository or a central API repository), split into files and **bundled** for publishing. Every **pull request** triggers: **validation** and **linting** (Spectral or Redocly) against the house style guide; a **breaking-change check** (oasdiff or similar) against the version on the main branch, requiring explicit approval or a major version bump for breaking changes; **example validation** so examples match their schemas; and, for implemented APIs, **contract tests** against a running build. After merge, the pipeline **publishes documentation** (Redoc or Swagger UI to the developer portal), **generates and publishes SDKs** with semantic versions, updates **mock servers** for consumers, and registers the API in a **catalogue**. Reviews include API consumers and an API design reviewer for public APIs. This turns documentation from a chore that drifts into the single source of truth that drives everything else.

## The API contract pipeline

Every change to the contract is linted, diffed, tested and then published as docs, SDKs and mocks.

![A horizontal pipeline of five connected stages, ending in three outputs branching to a book, a package and a mask icon.](assets/figures/openapi/section-8-map.svg) — Figure 8.1 — From pull request to published docs, SDKs and mocks.

## A GitHub Actions workflow for an OpenAPI contract

Lint, check for breaking changes and bundle on every pull request.

```yaml
name: api-contract
on:
  pull_request:
    paths: ["api/**"]

jobs:
  contract:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with: { fetch-depth: 0 }
      - uses: actions/setup-node@v4
        with: { node-version: 22 }
      - name: Lint
        run: npx @stoplight/spectral-cli lint api/openapi.yaml --fail-severity=error
      - name: Bundle
        run: npx @redocly/cli bundle api/openapi.yaml -o dist/openapi.yaml
      - name: Breaking-change check
        run: |
          git show origin/${{ github.base_ref }}:api/openapi.yaml > base.yaml
          docker run --rm -v "$PWD:/w" -w /w tufin/oasdiff breaking base.yaml api/openapi.yaml --fail-on ERR
      - uses: actions/upload-artifact@v4
        with: { name: openapi, path: dist/openapi.yaml }
```

## Make breaking changes a conscious decision

A failing breaking-change check should not be bypassed silently. Require a label, an approval from the API owner and a version bump, so breaking changes happen on purpose and with a migration plan.

**Quiz:** In an API-first pipeline, what does a breaking-change check compare?

- [ ] Two database schemas
- [ ] Swagger UI themes
- [x] The new OpenAPI document against the previous published version
- [ ] Server CPU usage

*Answer:* The new OpenAPI document against the previous published version. It diffs contracts to detect changes that would break existing clients.
