Migrate from Redocly CLI

This guide moves a repository that lints and bundles its OpenAPI descriptions with Redocly CLI to apigenie: the ruleset and the rules by name, redocly.yaml, your configurable rules, the decorators, the bundle, the report in CI, and what stays with Redocly. It starts from Redocly CLI 2.72 with its recommended ruleset and an OpenAPI file, openapi.yaml.

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

What changes and what stays

Redocly CLI API Genie
Lint redocly lint openapi.yaml apigenie lint openapi.yaml
Configuration redocly.yaml: extends, rules, apis apigenie.yaml, section commands.lint (Configuration)
Built-in rulesets minimal, recommended, recommended-strict, all, spec recommended, strict, all, per standard; 64 rules for OpenAPI
Severities error, warn, off error, warning, hint, off
Configurable rules rule/<name> with subject and assertions custom rules in Spectral’s format, given and then
Plugins JavaScript rules, decorators and preprocessors JavaScript functions, in a sandbox
Ignoring a problem .redocly.lint-ignore.yaml x-apigenie-lint-disable in the document
Decorators remove-x-internal, info-override, filter-in, … OpenAPI Overlays, applied by apigenie overlay
Bundle redocly bundle openapi.yaml -o dist/openapi.yaml apigenie bundle openapi.yaml --output dist/openapi.yaml
Report formats --format codeframe, stylish, json, junit, … --formatter stylish, json, junit, sarif, …
Exit status 1 on an error, 0 on warnings alone the same, and 2 on a file it cannot read
Fixes none --fix for the issues that have one
Configuration check redocly check-config apigenie config check
Documentation redocly build-docs openapi.yaml apigenie-generator docs openapi.yaml -o docs

Run both on the same file

redocly lint --format stylish openapi.yaml
apigenie lint openapi.yaml

With its recommended ruleset, Redocly reports three problems in a small file whose operation has no security requirement, no error response and an unused schema:

openapi.yaml:
  15:5  error    security-defined        Every operation should have security defined on it or on the root level.
  19:7  warning  operation-4xx-response  Operation must have at least one `4XX` response.
  33:5  warning  no-unused-components    Component: "Unused" is never used.

API Genie reports ten:

   3:3   error    Info summary is missing or empty                                  info-defined
   3:3   error    Info description is missing or empty                              info-defined
  11:5   warning  Tag 'pets' has no meaningful description defined                  tag-description-defined
  16:7   error    Operation has no meaningful description                           operation-description-defined
  16:7   warning  Security requirement is not defined                               operation-security-defined
  21:11  warning  Missing rate limiting header                                      operation-rate-limiting-defined
  27:19  error    Object schema does not disable additional properties              schema-no-additional-properties
  30:23  error    Schema with integer type does not define proper format            schema-format-defined
  34:7   warning  String schema does not define constraints restricting its length  schema-constraints
  34:7   warning  Unused shared component                                           unused-shared-component

  10 issues (5 errors, 5 warnings)
  1 errors, 1 warnings are potentially auto-fixable

Each line gives the position, the severity, the message and the rule; every rule has a page in the linter rules that says what it checks and why. The two reports differ, and not only in the names: without configuration API Genie applies the recommended ruleset, 43 rules that ask for descriptions, formats and bounds Redocly’s recommended does not, and it leaves out some of what Redocly reports, such as the missing error response. The next two sections say which.

Replace the configuration

A redocly.yaml that extends the built-in ruleset, adjusts a few rules and names the APIs:

extends:
  - recommended
rules:
  operation-4xx-response: off
  no-unused-components: error
  info-contact: warn
apis:
  orders@v1:
    root: apis/orders/openapi.yaml

becomes, in apigenie.yaml at the root of the repository:

version: '1.0'
commands:
  lint:
    extends: [recommended]
    rules:
      unused-shared-component: error
      contact-defined: warning
  • extends names API Genie’s rulesets, not Redocly’s. recommended is the default; strict turns on more rules with higher severities, all every rule. Redocly’s recommended-strict is recommended with every rule at error, which has no ruleset of its own: start from recommended and raise the rules that matter to error. minimal is recommended with most rules off, and spec, the struct rule alone, is openapi-json-schema-validation listed under rules without extends.
  • A rule takes its name from the tables below. Redocly’s warn is warning; error and off stay.
  • A rule with no counterpart, such as operation-4xx-response, has no line.
  • apis has no counterpart: one commands.lint section applies to every file the command is given. A repository whose APIs need different rules keeps a configuration file per API and passes it with --config, or disables rules in the documents, as below.
  • oas2Rules, oas3_0Rules, oas3_1Rules and async3Rules become the swagger and asyncapi sections next to rules: an OpenAPI 3.x file, a Swagger 2.0 file and an AsyncAPI file are linted by three linters with rules of their own, from one configuration (configuration of lint). arazzo1Rules have no counterpart: Arazzo files are not read.
  • --skip-rule on the command line is off in the configuration, and --extends is --config with another file. --max-problems has no counterpart.

The rules Redocly’s recommended ruleset reports as errors, and their counterparts in the OpenAPI linter. A rule marked not checked has no counterpart today: drop its line from the configuration.

Redocly API Genie Note
struct openapi-json-schema-validation
no-unresolved-refs openapi-json-schema-validation local schema $ref targets are checked; external references are not fetched
operation-operationId-unique operation-id-unique
operation-parameters-unique parameters-unique
path-parameters-defined path-parameters-defined path-parameters-on-items asks for them on the path item
security-defined operation-security-defined every operation has a security requirement
no-identical-paths paths-unique
no-path-trailing-slash, path-declaration-must-exist paths-well-formed also rejects // and upper-case letters outside the placeholders
path-not-include-query not checked
no-enum-type-mismatch schema-enum-types
no-schema-type-mismatch, no-example-value-and-externalValue not checked
no-empty-servers servers-defined
no-server-trailing-slash, no-server-variables-empty-enum, no-undefined-server-variable not checked server-urls checks the scheme and the case of the URL instead
operation-summary not checked the custom rule below
operation-operationId-url-safe, spec-components-invalid-map-name not checked

The rules it reports as warnings:

Redocly API Genie Note
info-license, info-license-strict license-defined asks for name, and url or identifier
no-ambiguous-paths paths-unique
no-duplicated-enum-values schema-enum-no-duplicates
no-duplicated-tag-names tags-unique
no-required-schema-properties-undefined schema-required-properties-defined schema-no-contradictions adds the case of additionalProperties: false
no-unsafe-markdown safe-markdown
no-unused-components unused-shared-component follows references transitively, so a component used only by an unused one is reported too
operation-2xx-response operation-success-response
operation-4xx-response not checked standard-response-errors checks the format of the error responses that are there
operation-operationId operation-id-defined
tag-description tag-description-defined
no-invalid-media-type-examples, no-invalid-parameter-examples, no-invalid-schema-examples not checked examples are not validated against their schema
no-illogical-composition-keywords, no-mixed-number-range-constraints not checked schema-no-contradictions reports a schema that rejects every value, schema-discriminator-defined a oneOf without discriminator
no-server-example.com, security-scopes-defined, spec-ref-siblings not checked

The rules Redocly leaves off by default:

Redocly API Genie
info-contact contact-defined
operation-description operation-description-defined
parameter-description parameter-description-defined
path-params-defined path-parameters-defined
tags-alphabetical tags-sorted
request-mime-type, response-mime-type standard-media-types, which checks that the media types are registered ones rather than a list of yours
required-string-property-missing-min-length schema-constraints
scalar-property-missing-example schema-example-defined
operation-4xx-problem-details-rfc7807 standard-response-errors
response-contains-header not checked; operation-rate-limiting-defined asks for the rate-limiting headers
component-name-unique, no-http-verbs-in-paths, operation-singular-tag, operation-tag-defined, paths-kebab-case, path-http-verbs-order, path-segment-plural, boolean-parameter-prefixes, array-parameter-serialization, response-contains-property, spec-strict-refs, spec-example-values not checked

The oas2Rules apply to Swagger 2.0 files, which API Genie lints with the Swagger 2.0 linter: struct is swagger-validation, which also reports the unresolved references, and the rules that share a name with an OpenAPI rule check the same thing. The async3Rules map onto the AsyncAPI linter the same way.

Rules of recommended that Redocly’s has nothing for; each has a page that says what it checks and why:

Outside recommended: spell-check for the texts written for humans, schema-keyword-support for the JSON Schema keywords the tools around the document ignore, and the privacy ruleset for credentials and personal data in URLs. extends: [recommended, privacy] adds the last one.

Carry your configurable rules over

A configurable rule of redocly.yaml names the nodes it checks with subject and what it asserts about them:

rules:
  rule/operation-summary:
    subject:
      type: Operation
      property: summary
    assertions:
      defined: true
    severity: error
    message: Operations must have a summary

It becomes a custom rule under commands.lint.rules, in the format of Spectral: a JSONPath expression selects the nodes, a function checks each of them:

version: '1.0'
commands:
  lint:
    extends: [recommended]
    rules:
      operation-summary:
        description: Operations must have a summary
        message: "{{property}} is missing at {{path}}"
        severity: error
        given: "$.paths[*][get,put,post,delete,options,head,patch,trace]"
        then:
          field: summary
          function: truthy
  • subject.type becomes the given expression that selects the same nodes, subject.property the field, and subject.filterInParentKeys a member union in the expression. A where condition becomes a JSONPath filter (JSONPath).
  • The assertions map onto the core functions: defined is truthy or defined, pattern and notPattern are pattern with match or notMatch, enum is enumeration, casing is casing, minLength and maxLength are length, mutuallyExclusive is xor, sortOrder is alphabetical. required, disallowed, requireAny, mutuallyRequired, nonEmpty and ref have no function: write a JavaScript function of a few lines.
  • severity and message stay; {{property}}, {{path}} and {{value}} fill the message.

A rule of a custom plugin is a JavaScript visitor over the document’s node types; it is rewritten as a custom rule whose function is a JavaScript module in functions/ next to apigenie.yaml. Functions run in a sandbox with no file system, network, processes or timers, and a call must finish within 5 seconds. The decorators and preprocessors of a plugin become overlays, next.

Replace the decorators with overlays

Decorators change the document on the way to the bundle and the documentation: remove-x-internal drops what is marked internal, info-override and filter-in or filter-out edit and select. API Genie writes these changes as an OpenAPI Overlay, a file of actions that each select nodes with a JSONPath query and change them:

overlay: 1.0.0
info:
  title: Remove the internal operations
  version: 1.0.0
actions:
  - target: $.paths[*][?(@["x-internal"] == true)]
    remove: true
  - target: $.paths[?(@["x-internal"] == true)]
    remove: true
  - target: $.info
    update:
      title: Public API
      description: The public operations only.
apigenie overlay --overlay public.overlay.yaml openapi.yaml --output dist/openapi.yaml

The first two actions remove the operations and the path items marked x-internal: true, the third edits info; YAML comments of the input are kept. The code generators take an overlay directly, with --overlay or the overlay setting of a target (Overlays), so the public version of the document need not be written to disk for them.

Bundle

redocly bundle openapi.yaml -o dist/openapi.yaml
apigenie bundle openapi.yaml --output dist/openapi.yaml

Both copy the schemas, parameters and responses of other local files into components, named after their files, and rewrite the references; the extension of the output file picks JSON or YAML. bundle does not keep YAML comments, and references by URL are not supported. --dereferenced, --remove-unused-components, --keep-url-references, --ext and --metafile have no counterpart, and decorators don’t run during the bundle: apply the overlay to the bundled file in a second command.

Disable a rule in one place

Redocly keeps the problems to ignore in .redocly.lint-ignore.yaml, written by --generate-ignore-file. API Genie turns the rule off where the issue is, in the document, with x-apigenie-lint-disable on the object and its descendants:

paths:
  /legacy/export:
    x-apigenie-lint-disable:
      - operation-id-defined
    get:
      summary: Export, as it was

Disable a rule as close to the issue as possible; disabling rules says more.

The report in CI

The formats that keep their name: --format stylish, json, checkstyle and junit become --formatter stylish, json, checkstyle and junit. API Genie adds sarif, html, compact, tap, unix and visualstudio; Redocly’s codeframe (its default), codeclimate, summary, markdown and github-actions have no counterpart. For GitLab, junit is what the merge request reads; for GitHub code scanning, sarif (formatters). The exit status stays what it was: 1 when an error is reported, 0 on warnings alone (exit status).

On GitHub Actions, the run step with --format github-actions becomes the GitHub Action, which installs the CLI and shows the issues on the lines of the pull request:

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

What stays with Redocly

  • push, project, preview, login, eject and translate work with the Redocly platform and its projects. build-docs becomes apigenie-generator docs, a static site with search and Try it out, built with npm and served from any static host.
  • split and join have no counterpart; apigenie bundle goes one way.
  • generate-client becomes apigenie-generator client fetch, and diff becomes apigenie diff; Migrate from oasdiff covers the move of a breaking-change check in detail.
  • respect and generate-arazzo run and write Arazzo workflows, which are not read; apigenie test checks a running server against the description with tests of its own format.
  • proxy, drift and generate-spec work on recorded traffic. The validating proxy of apigenie-server checks live traffic against the description; inferring a description from traffic has no counterpart.
  • stats, score and scorecard-classic have no counterpart.

Remove Redocly CLI

When the two reports agree on what matters to you: delete redocly.yaml and .redocly.lint-ignore.yaml, remove @redocly/cli from the development dependencies, and replace the Redocly step of the pipeline with the one above. The fixes the linter offers are a --fix away:

apigenie lint --fix openapi.yaml
Was this helpful?