API Linting with Vacuum
This example demonstrates how to lint your Zuplo API using Vacuum, an OpenAPI linter. It includes both built-in OpenAPI rules and custom rules specific to Zuplo projects, such as requiring certain policies on all routes.
Prerequisites
Working with this Example
Locally
Working locally is the best way to explore and understand the code for this example. You can get a local version by using the Zuplo CLI:
npx create-zuplo-api@latest --example api-linting
Then, in the project directory run the following commands:
Deploy this example to Zuplo
It is also possible to deploy this example directly to your Zuplo account and work with it via the Zuplo Portal. You can do this by clicking the Deploy to Zuplo button anywhere on this page.
How It Works
Ruleset Configuration
The linting rules are defined in config/api-ruleset.yaml. This file configures both standard OpenAPI rules and custom Zuplo-specific rules.
Built-in Rules
Standard OpenAPI validation rules can be enabled:
rules:
oas3-schema: true
oas3-api-servers: false
oas3-unused-component: false
Custom Zuplo Rules
This example includes custom rules that enforce Zuplo-specific requirements:
1. Valid Path Mode
Ensures x-zuplo-path.pathMode is either open-api or url-pattern:
zuplo-valid-path-mode:
description: Zuplo pathMode must be 'open-api' or 'url-pattern'
given: $.paths.*.x-zuplo-path
severity: error
then:
field: pathMode
function: pattern
functionOptions:
match: "^(open-api|url-pattern)$"
2. Required Route Configuration
Ensures all operations have x-zuplo-route with required properties:
zuplo-route-required-properties:
description: Zuplo route must have required properties
given: "$.paths[*]['get','put','post','delete','options','head','patch','trace'].x-zuplo-route"
severity: error
then:
- field: corsPolicy
function: truthy
- field: handler
function: truthy
- field: policies
function: truthy
3. Required Policy
Ensures all routes have a specific policy (e.g., authentication):
zuplo-require-policy:
description: Require all routes have a specific zuplo policy
severity: error
given: "$.paths[*]['get','put','post','delete','options','head','patch','trace'].x-zuplo-route.policies.inbound"
then:
function: zuploRequiredPolicy
functionOptions:
policyName: "my-auth-policy"
4. Path Prefix
Ensures all paths start with a specific value (e.g., /v1):
path-starts-with:
description: Require all paths to start with a specific value
severity: error
given: "$.paths"
then:
function: pathStartsWithPolicy
functionOptions:
startsWith: "/v1"
Project Structure
├── config/
│ ├── api-ruleset.yaml # Linting rules configuration
│ ├── policies.json # Policy configurations
│ └── routes.oas.json # Route definitions to lint
└── lint-functions/
├── zuploRequiredPolicy.js # Custom function: require specific policy
└── pathStartsWithPolicy.js # Custom function: validate path prefix
Running the Linter
Run the linter against your routes:
Example output when rules fail:
ERROR: zuplo-require-policy - Require all routes have a specific zuplo policy
at $.paths./v1/todos.post.x-zuplo-route.policies.inbound
ERROR: path-starts-with - Require all paths to start with a specific value
at $.paths./users
Customizing Rules
Change Required Policy Name
Edit config/api-ruleset.yaml:
zuplo-require-policy:
then:
function: zuploRequiredPolicy
functionOptions:
policyName: "api-key-auth" # Change to your policy name
Change Required Path Prefix
path-starts-with:
then:
function: pathStartsWithPolicy
functionOptions:
startsWith: "/api/v2" # Change to your prefix
Add More Custom Functions
Create a new JavaScript file in lint-functions/ and reference it in your ruleset:
// lint-functions/myCustomRule.js
export default function (input, options) {
// Return array of errors, or empty array if valid
return [];
}
CI/CD Integration
Add linting to your CI pipeline to catch issues before deployment:
# GitHub Actions example
- name: Lint API
run: npm run lint
Learn More