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 named email-required.
  • message: Specify the error message to display.
  • given: $.components.schemas.User.properties.email: Target the email property within the User schema.
  • severity: error: Mark violations of this rule as errors.
  • then: Define what the rule checks for.
  • field: required: Look at the required field of the target property.
  • function: truthy: Assert that the required field must be truthy (meaning it should be present and likely true if 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.

Want structured learning?

Take the full Swagger course →