Migrate from oasdiff

This guide moves a repository that checks the changes of its OpenAPI description with oasdiff to apigenie diff: the commands, the levels and the exit status, the checks as rules, deprecation and sunset dates, the report in CI, and what has no counterpart. It starts from oasdiff 1.35 and two versions of an OpenAPI file, v1.yaml and v2.yaml.

You need the CLI installed; the Quickstart shows how. Keep oasdiff installed until the last step, so you can compare the two reports on the same pair of files.

What changes and what stays

oasdiff API Genie
Breaking changes oasdiff breaking v1.yaml v2.yaml apigenie diff v1.yaml v2.yaml
Changelog oasdiff changelog v1.yaml v2.yaml the same command: the changelog is the default report
Every change oasdiff diff, oasdiff summary --report detailed, --report status
Levels error, warning, info breaking, non-breaking, non-functional (severities)
Exit status 0, unless --fail-on ERR or --fail-on WARN 1 on a breaking change, 0 otherwise, 2 on a file it cannot read
Checks 780 checks by id, listed by oasdiff checks changelog a rule per node type and property, such as operation.parameters.add_item (rules)
Configuration .oasdiff.yaml, --severity-levels, --err-ignore, --warn-ignore apigenie.yaml, section commands.diff (Configuration)
Deprecation x-sunset, --deprecation-days-stable, --deprecation-days-beta x-sunset, the sunset_passed condition, --today
Report formats --format text, markdown, html, json, junit, githubactions, yaml, … --formatter stylish, markdown, html, json, unix, visualstudio
Standards OpenAPI 3.x OpenAPI 3.x, Swagger 2.0, AsyncAPI 3.x, Protobuf and GraphQL, each with rules of its own
Version api-major-version-not-bumped reports a missing bump --update-version writes the version the changes call for
History two files at a time apigenie changelog openapi.yaml reads the versions from git (changelog)

Run both on the same pair

Between v1.yaml and v2.yaml, GET /orders gets a required status parameter, the Order schema of the responses loses its legacyCode property, and GET /legacy/export, deprecated with x-sunset: "2026-09-30", is removed after that date.

oasdiff breaking v1.yaml v2.yaml
apigenie diff v1.yaml v2.yaml
1 changes: 1 error, 0 warning, 0 info
error	[new-required-request-parameter] at v2.yaml
	in API GET /orders
		added the new required `query` request parameter `status`
v2.yaml
  45  7  non-breaking  Removed operation GET /legacy/export
  10  7      breaking  Modified operation GET /orders
  10  7      breaking  Added parameter status in query
  10  7      breaking  Modified response code 200
  33  7      breaking  Modified operation GET /orders/{orderId}
  33  7      breaking  Modified response code 200

Both rate the required parameter breaking, and both let the deprecated operation go: oasdiff leaves the removal out of its breaking report, API Genie lists it as non-breaking. The removed property is where they differ. oasdiff rates response-optional-property-removed as info; the recommended rules rate a property removed from a response as breaking, since a client may read it, unless the property was deprecated and its x-sunset date has passed. --report detailed names the rule behind each line, schema.properties.remove_item in the response layer here, so a disagreement is settled in the configuration, next. The exit status differs too: apigenie diff exits with 1 on any breaking change, where oasdiff needs --fail-on.

Replace the configuration

The severity of every change is a rule, so the flags and files that tune oasdiff become settings of commands.diff in apigenie.yaml at the root of the repository. Here a changed title is non-functional rather than breaking, and a removed response property is non-breaking, as oasdiff has it:

version: '1.0'
commands:
  diff:
    extends: [recommended]
    rules:
      info:
        title: non_functional
    response:
      schema:
        properties:
          remove_item: non_breaking
  • --fail-on ERR has no flag: the command exits with 1 on a breaking change. oasdiff’s warnings are changes that may break a client, such as request-parameter-removed; the recommended rules rate most of them breaking already, so --fail-on WARN is the default exit status too. A change rated too low or too high gets another severity in rules, or in request and response for the schemas of parameters and bodies and those of responses (direction).

  • --severity-levels, --err-ignore and --warn-ignore match the lines of a report; a rule names the property instead. none leaves a change out of the status and the changelog, non_functional keeps it in the changelog without failing the check.

  • --match-path, --unmatch-path and --filter-extension become overrides, a list of rules applied to the locations that match a JSON pointer pattern, where * matches a part of a segment and ** any number of segments. These leave the internal paths out:

      overrides:
        - where:
            location: /paths/~1internal~1*/**
          rules:
            pathItem:
              operations: none
  • --exclude-elements description,summary,examples,extensions is close to the default: descriptions, summaries, examples and extensions are non_functional, in the changelog and never failing. none on the property, operation: {description: none}, leaves them out.

  • --auto-upgrade has no counterpart: a 3.0 and a 3.1 document are compared as they are, and the changed version is one more change, rated breaking by the openapi rule of the openapi node; none on it leaves it out.

  • --flatten-allof, --flatten-params, --include-path-params, --composed, --prefix-base, --strip-prefix-base, --stability-level, --attributes, --lang and --template have no counterpart.

Deprecate, then remove

Both tools read x-sunset, so the deprecations already in the document carry over. What is checked differs:

  • oasdiff reports deprecating an operation without a sunset date (api-deprecated-sunset-missing), a sunset date closer than --deprecation-days-stable or --deprecation-days-beta (api-sunset-date-too-small), and a removal before the date (api-removed-before-sunset).
  • API Genie rates deprecating as non-breaking, with or without a date, and the removal by the sunset_passed condition: a deprecated operation, path item, parameter, component schema, response property or request enum value may go once its x-sunset date is on or before the date of the comparison, and is breaking before it. How far ahead the date is set is for the reviewer of the pull request, not a rule.

--today 2027-02-01 runs the comparison as of another date, to see a removal pass before then, and apigenie evolve plan writes the deprecations and the removal in two waves. Accepting a breaking change lists the other ways forward.

The checks, as rules

The oasdiff checks most pull requests meet, and the rule that rates the same change; each rule is a line of the OpenAPI tables, where the configuration changes it. Checks whose id differs only by the kind of element, such as request-property-removed and response-property-removed, follow the same rows.

oasdiff Level API Genie Note
api-removed-without-deprecation, api-path-removed-without-deprecation error pathItem.operations.remove_item, paths.remove_item breaking, unless sunset_passed
api-removed-before-sunset, api-path-removed-before-sunset error the same
api-path-removed-with-deprecation info the same non-breaking once the date has passed
api-deprecated-sunset-missing, api-sunset-date-too-small error not checked operation.deprecated is non-breaking
endpoint-added info pathItem.operations.add_item non-breaking
new-required-request-parameter error operation.parameters.add_item breaking when required
new-optional-request-parameter info the same non-breaking otherwise
request-parameter-removed warning operation.parameters.remove_item breaking, unless sunset_passed
request-parameter-became-required error parameter.required
request-parameter-type-changed, response-property-type-changed error schema.type breaking in both directions
request-property-became-required, request-property-became-required-with-default error schema.required.add_item in request the default does not matter
response-required-property-removed error schema.properties.remove_item in response breaking, unless sunset_passed
response-optional-property-removed info the same breaking too; the configuration above lowers it
response-property-became-optional error schema.required.remove_item in response
response-success-status-removed error responses.remove_item every removed response code is breaking
api-security-removed error operation.security.remove_item
api-operation-id-removed info operation.operationId breaking: generated clients name their functions after it
api-major-version-not-bumped info --update-version writes the version instead of checking it

The report in CI

On GitHub Actions, the oasdiff/oasdiff-action/breaking step with its base and revision becomes the GitHub Action with command: diff. It compares each file with its version at the base commit of the pull request and shows the changes on its lines; base picks another commit, branch or tag, base-file another file of the repository, and report one of the three reports:

on: pull_request
jobs:
  api:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: mykulyak/api-genie-action@v0
        with:
          command: diff
          files: openapi.yaml

Elsewhere, take the base version from git and compare the two files; the exit status fails the job on a breaking change:

git show main:openapi.yaml > main.yaml
apigenie diff main.yaml openapi.yaml

The formats that keep their name: --format markdown, html and json become --formatter markdown, html and json; text, the default, is stylish, the default. API Genie adds unix and visualstudio; githubactions is what the action’s annotations do, and junit, yaml, singleline and markup have no counterpart. A script reads the status alone:

apigenie diff --report status --formatter json main.yaml openapi.yaml
{
  "formatVersion": 1,
  "status": "breaking"
}

Release notes

oasdiff changelog v1.yaml v2.yaml --format markdown becomes apigenie diff --formatter markdown v1.yaml v2.yaml, the changelog of two files. For the notes of a release, apigenie changelog reads the versions of the file from git instead, with an entry per commit that changes the API or, with --revisions tags, per release tag, each change rated with the same rules.

What stays with oasdiff

  • oasdiff diff prints every difference as YAML or JSON, without a severity. --report detailed --formatter json is the nearest: every change with its position, the rule and the severity.
  • oasdiff summary counts the differences by kind; --report status says only whether a change is breaking.
  • flatten, upgrade, breaking-files and the git diff driver have no counterpart; validate is apigenie lint.
  • --composed, which compares every file matching two globs as one API, has no counterpart: compare the files one pair at a time, or bundle each version first.
  • --lang translates the messages; API Genie’s reports are in English.

Remove oasdiff

When the two reports agree on what matters to you: delete .oasdiff.yaml and the ignore files, remove the oasdiff binary or image from the pipeline, and replace its step with the one above. Before the next release, let the differ write the version the changes call for:

apigenie diff --update-version main.yaml openapi.yaml
Was this helpful?