Enforcing OpenAPI Linting with Spectral in CI
AI generated
{ }
GET
OpenAPI · CI/CD
Enforcing OpenAPI Linting with Spectral in CI
How consistency rules for the API specification get enforced automatically instead of through manual code reviews

An OpenAPI specification checked only occasionally by manual code review drifts into inconsistency over time almost inevitably: mismatched field naming, missing error response definitions, inconsistent path structures. Spectral, a dedicated OpenAPI linting tool, makes these consistency rules machine-checkable and automatically enforceable in the CI pipeline, instead of relying on individual reviewers' attention.

14 min read Spectral · OpenAPI Linting CI/CD

1. Why manual reviews aren't enough for API consistency

A reviewer checking a new OpenAPI definition needs to keep implicit knowledge of all prior naming conventions, error formats, and structural decisions in their head, which becomes increasingly unrealistic as teams and API surfaces grow. Small inconsistencies (sometimes camelCase, sometimes snake_case, sometimes a missing 404 response definition) creep in gradually, not because of a single review mistake, but because of the sheer volume of details to check.

An automated linting tool like Spectral codifies these conventions once as a machine-readable ruleset and checks every new or changed OpenAPI definition against it consistently and without fatigue, letting reviewers focus on substantive, non-mechanical aspects while stylistic and structural consistency is ensured automatically.

This effect intensifies further once multiple teams work simultaneously on different parts of the same API, since a central, automatically enforced ruleset prevents each team from implicitly developing its own, slightly diverging conventions that would later need to be painstakingly unified again.

2. Spectral basics: built-in rulesets as a starting point

Spectral ships several built-in rulesets, including spectral:oas for general OpenAPI best practices (such as mandatory operationId, mandatory descriptions) and spectral:asyncapi for AsyncAPI specifications. These built-in rules cover a solid baseline but should almost always be supplemented with project-specific rules that reflect the concrete naming conventions and structural decisions of your own API.

Configuration happens via a .spectral.yaml file at the project root, which extends built-in rulesets and adds custom, individual rules, giving a team full control over the actually enforced ruleset instead of relying entirely on a generic default configuration. This file should be versioned like any other project configuration and maintained in the same repository as the OpenAPI specification.


# .spectral.yaml
extends: [[spectral:oas, all]]

rules:
  path-must-be-kebab-case:
    description: API paths must use kebab-case
    given: "$.paths[*]~"
    severity: error
    then:
      function: pattern
      functionOptions:
        match: "^\/[a-z0-9\-\/{}]+$"

  operation-must-have-error-response:
    description: Every operation needs a 4xx error response
    given: "$.paths[*][*].responses"
    severity: warn
    then:
      field: "4XX"
      function: truthy

  no-http-verbs-in-path:
    description: Paths must not contain HTTP verbs
    given: "$.paths[*]~"
    severity: error
    then:
      function: pattern
      functionOptions:
        notMatch: "(get|create|update|delete)"

3. Writing custom rules for project-specific conventions

The biggest practical benefit of Spectral comes from custom, project-specific rules that go beyond generic best practices: mandatory pagination parameters for all list endpoints, a unified error response schema for all operations, mandatory rate limit header documentation. Each of these rules codifies a design decision the team has already made, but which would otherwise need to be manually re-enforced for every new endpoint without automation.

Spectral rules use JSONPath expressions (given) to select relevant parts of the specification, and functions (then) like truthy, pattern, length, or custom JavaScript functions to perform the actual check. This flexibility allows encoding practically any conceivable structural convention as an automated rule. An iterative development process, where new rules are first tested against a small, representative sample of existing endpoints, prevents an unexpected flood of false positives on the first production run.

4. Integrating Spectral into the CI pipeline

Integration into GitHub Actions or GitLab CI is straightforward: a CI step runs spectral lint openapi.yaml and fails the build on rule violations with severity error, while violations with severity warn don't block the build but appear visibly in the CI log. This graduated severity allows introducing new rules as warnings first, before they become hard errors after a transition period.

It's important to run linting as early as possible in the development process, ideally already as a pre-commit hook or directly in the IDE via a Spectral extension, instead of discovering violations only in the CI run after the push, once the developer's context has already shifted away.

5. Breaking change detection as a complement to pure style linting

Spectral itself primarily checks the style and structure of a single specification version, but doesn't automatically detect whether a change compared to the previous version is backward-compatible. For this task, a separate tool like openapi-diff or oasdiff is usually used, comparing two OpenAPI versions and explicitly distinguishing between breaking and non-breaking changes (say, a removed required field as breaking, a new optional field as non-breaking).

The combination of Spectral for style consistency and a dedicated diff tool for breaking change detection covers two distinct but equally important aspects of API quality assurance and should run as two separate, sequential steps in the CI pipeline.

6. Gradual introduction into an existing, unchecked API

For an already existing, large OpenAPI specification with many historically grown inconsistencies, immediate, full rule enforcement typically produces hundreds of error messages, which tends to discourage rather than motivate the team. A more pragmatic approach is to first apply new rules only to newly added or changed parts of the specification, while leaving existing, unchanged parts exempt from checking for now.

Spectral doesn't directly support this gradual approach out of the box, but can be combined with a Git-diff-based CI script that checks only the actually changed paths of the OpenAPI file against the ruleset, while unchanged legacy areas are temporarily ignored until they get reworked as part of other work anyway.

7. Securing team buy-in through clear error messages

A Spectral rule that only delivers a cryptic error message without context frustrates developers and undermines acceptance of the entire linting process. Every custom rule should therefore include a clear, helpful description that not only describes what's wrong but also what it should look like correctly, ideally with a link to internal API design documentation for more detailed explanations.

A team that treats its Spectral rules as living documentation of design decisions, rather than a pure enforcement tool, additionally benefits from new team members effectively learning the API conventions through the rule error messages themselves, instead of having to read separate documentation.

8. Versioning and centrally maintaining the ruleset itself

With multiple teams each maintaining their own OpenAPI specifications, it's worth publishing the Spectral ruleset itself as its own, versioned npm package, instead of copying it separately into every repository and inevitably letting it drift apart. Each team repository then extends this central package, so rule changes get maintained in a single place and roll out to all dependent projects via a regular package version update.

This centralized approach requires a clear governance process for changes to the shared ruleset, for example via a dedicated API guild team that collects proposals from individual product teams and incorporates them into the central ruleset, instead of letting individual teams change rules uncoordinated.

9. Spectral at a glance

The table below summarizes the key use cases.

Aspect Tool Purpose
Style and structure Spectral with built-in and custom rules Consistent naming conventions, mandatory fields
Breaking changes openapi-diff or oasdiff Detection of backward-incompatible changes
CI integration GitHub Actions / GitLab CI Automated checking on every pull request
IDE integration Spectral VS Code extension Immediate feedback during development

Mironsoft

OpenAPI design, Symfony APIs, and API security

APIs that external teams can integrate without back-and-forth questions?

We review existing REST APIs for inconsistent error formats, missing OpenAPI documentation, and security gaps, then build an API that is clearly documented, versioned, and hardened against abuse.

API Review

Checking the OpenAPI spec, error formats, and status codes for consistency.

Symfony Implementation

Using DTOs, Serializer, and Validator for clean, type-safe request/response models.

Security Audit

Hardening rate limiting, auth schemes, and input validation against real attack surfaces.

10. Summary

Spectral Linting: The Essentials at a Glance

Why linting

Manual reviews aren't enough to reliably enforce consistency as the API surface grows.

Custom rules

The biggest benefit comes from project-specific rules that codify already-made design decisions.

CI mandatory

Severity error blocks the build on violations, severity warn allows a gradual rollout.

Complements breaking-change tools

Spectral checks style, a separate diff tool checks backward compatibility between versions.

11. FAQ: Spectral Linting: The Essentials at a Glance

1Is Spectral free to use?
Yes, Spectral is open source and free, both as a CLI tool and via the available editor integrations.
2Do I have to adopt all built-in OAS rules?
No, individual rules can be selectively disabled if they don't fit your own conventions.
3How do I write a custom Spectral function?
Via JavaScript functions referenced as custom functions in the ruleset, for checks the built-in functions don't cover.
4Can Spectral also check AsyncAPI specifications?
Yes, via the built-in spectral:asyncapi ruleset, analogous to the OpenAPI ruleset.
5Should linting really block the build?
For critical rules (missing error handling, inconsistent paths) yes. For stylistic details, a warning can be enough.
6How do I handle a huge, unchanged legacy specification?
With a gradual approach that only checks new or changed parts, instead of immediately fully enforcing the entire specification.
7Does Spectral also detect semantic errors in example data?
Limited, primarily through schema validation. For deeper checking, additional contract testing tools are useful.
8How do I test my own Spectral rules?
With test cases that deliberately run valid and invalid example specifications against the rule and check the expected outcome.
9Does Spectral work with multiple, distributed OpenAPI files?
Yes, via $ref references between files, Spectral resolves these references during checking.
10Does Spectral replace full contract tests?
No, Spectral only checks the specification itself, not the actual behavior of the running API against that specification.