Swagger Spectral linting is surprisingly effective at catching not just stylistic inconsistencies, but also potential runtime bugs in your OpenAPI definitions.
Let’s see it in action. Imagine you have a simple OpenAPI v3 definition for a basic user resource:
openapi: 3.0.0
info:
title: User API
version: 1.0.0
paths:
/users:
get:
summary: List all users
operationId: listUsers
responses:
'200':
description: A list of users.
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/User'
post:
summary: Create a new user
operationId: createUser
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/User'
responses:
'201':
description: User created successfully.
content:
application/json:
schema:
$ref: '#/components/schemas/User'
/users/{userId}:
get:
summary: Get a user by ID
operationId: getUserById
parameters:
- name: userId
in: path
required: true
schema:
type: integer
format: int64
description: The ID of the user to retrieve.
responses:
'200':
description: User details.
content:
application/json:
schema:
$ref: '#/components/schemas/User'
components:
schemas:
User:
type: object
properties:
id:
type: integer
format: int64
name:
type: string
email:
type: string
format: email
required:
- id
- name
Now, let’s introduce a common oversight. Suppose we define our User schema but forget to specify the email property as required, even though the format: email implies it should be present for a valid user.
We can use Spectral to enforce rules. First, you’ll need to install Spectral:
npm install -g @stoplight/spectral-cli
Next, create a Spectral configuration file, let’s call it .spectral.yaml:
extends: spectral:oas
rules:
email-required:
message: "The 'email' property must be present and non-empty."
given: $.components.schemas.User.properties.email
severity: error
then:
field: required
function: truthy
This configuration tells Spectral to:
extends: spectral:oas: Start with the standard OpenAPI ruleset.email-required: Define a new rule namedemail-required.message: Specify the error message to display.given: $.components.schemas.User.properties.email: Target theemailproperty within theUserschema.severity: error: Mark violations of this rule as errors.then: Define what the rule checks for.field: required: Look at therequiredfield of the target property.function: truthy: Assert that therequiredfield must be truthy (meaning it should be present and likelytrueif explicitly set, or implicitly required by context).
Let’s run Spectral against our openapi.yaml file:
spectral lint openapi.yaml
If our User schema looked like this (without required: [email]):
components:
schemas:
User:
type: object
properties:
id:
type: integer
format: int64
name:
type: string
email:
type: string
format: email # Missing 'required: true' or inclusion in a parent 'required' array
required:
- id
- name
Spectral would output an error:
✖ email-required
The 'email' property must be present and non-empty.
Given: $.components.schemas.User.properties.email
To fix this, we update the User schema to explicitly mark email as required:
components:
schemas:
User:
type: object
properties:
id:
type: integer
format: int64
name:
type: string
email:
type: string
format: email
required:
- id
- name
- email # Added email to the required array
Running spectral lint openapi.yaml again would now pass the email-required rule.
The real power of Spectral comes from its extensive catalog of pre-built rules and its flexibility to define custom ones. You can enforce naming conventions, ensure consistent parameter definitions, check for deprecated fields, and much more. For instance, a common rule is to ensure that all operationId values are unique and follow a specific camelCase format. Spectral can also catch logical inconsistencies, like defining a response schema that contradicts the data type specified in a parameter.
Spectral’s ability to work with JSON Schema allows it to understand the structure and types within your OpenAPI document deeply. When you define format: email for a string, Spectral’s built-in rules (or your custom ones) can infer that this field should be present and conform to an email pattern. If it’s missing from the required array, it’s a potential runtime error waiting to happen – your API might accept user objects without emails, leading to unexpected behavior or validation failures downstream. Spectral acts as a linter for your API’s contract, ensuring that the definition accurately reflects the intended behavior and constraints.
Beyond basic validation, Spectral can enforce design-by-contract principles. For example, you might have a rule that requires a description for every parameter and schema property. This forces developers to document their APIs thoroughly, making them easier to understand and use. Another useful application is ensuring consistent use of HTTP status codes – for instance, mandating that a 404 Not Found response is always included for operations that retrieve a specific resource by ID.
A subtle but powerful aspect of Spectral is its ability to handle OpenAPI extensions and custom formats. If you’re using vendor-specific extensions or defining custom data formats, Spectral can be configured to validate these as well, ensuring that even non-standard elements adhere to your team’s established patterns. This is crucial for maintaining consistency across complex, multi-team API projects.
The next step after mastering linting is to integrate Spectral into your CI/CD pipeline, automatically failing builds that introduce API definition inconsistencies.