Playground
Optic diffs your OpenAPI documents and lets you write tests about the changes. This playground simulates the kind of diffs Optic runs in CI (between the HEAD and BASE versions of an OpenAPI):
optic diff openapi.yaml --base main
Limitations: the playground can only load internal $ref files for security reasons. The actual CLI can resolve references from the file system and over http.
Examples of OpenAPI changes to make
- Remove a required property (Optic will show a breaking change)
- Remove an operation (Optic will show a breaking change)
- Add a new operation (Optic will show
added
) - Add a new query parameter (Optic will show
added
, and breaking if you made itrequired
) - Change the order of parameters, properties, or operations (Optic will show no changes)
How the diff worksβ
Optic computes a semantic API diff that highlights how the API has changed, not how the lines have changed (git) or the JSON has changed (json diff). Instead of being grouped by files/lines, our changelog shows you changes grouped by operation and body. If a $ref
is updated, the changelog shows you everywhere that $ref
is being used.
To compute its semantic diffs, Optic traverses the OpenAPI document and emits facts
about your API. Each fact describes a part of your API and is keyed with a deterministic ID. There are facts every property, parameter, operation, body, status code, etc (see code here). When Optic runs a diff it collects facts from both versions of the specification and compares them to one another.
- Facts that appear in both versions are compared to one another. Those with changes are marked Changed
- Facts that only appear in the latest version are marked Added
- Facts that only appear in the previous version are marked Removed
Testing changes with Opticβ
Optic makes it easy to lint your API design and test your changes. Our SDK lets you test for breaking changes and ensure your versioning policy is followed. You can also use the tests to make sure a style guide is followed whenever your team is building something new.
This test flags removed operations as breaking:
operations.removed(() => {
throw new RuleError({
message: "cannot remove an operation. This is a breaking change.",
});
});
This test flags adding a required query parameter as breaking:
queryParameter.added((parameter, ruleContext) => {
// allow required query parameters when operation is also being added
if (ruleContext.operation.change === "added") return;
if (parameter.value.required) {
throw new RuleError({
message: `cannot add required query parameter ${parameter.value.name} to an existing operation. This is a breaking change.`,
});
}
});
This test runs all the time and makes sure operationId
is set:
operations.requirement((operation) => {
if (!operation.value.operationId) {
throw new RuleError({
message: `operations must have an operationId. Missing for ${operation.method} ${operation.path}`,
});
}
});