Linting Your API
- Applies to
- Gatewayruntime for API, AI, and MCP gateways
- Hosting
- Managed EdgeManaged DedicatedSelf-Hosted
A Zuplo gateway is mostly configuration: OpenAPI files in config/ describe
your routes, and config/policies.json defines the policies those routes run. A
typo in that configuration — a policy name that doesn't exist, a required option
left out, a route with no authentication — doesn't fail a type check. It shows
up later, as a failed build or a route that behaves differently than you expect.
zuplo lint catches those problems on your machine or in CI, before you deploy.
It reads the same files the gateway reads and reports each problem with the
file, line, and column where it occurs.
Code
What the linter checks
The linter runs three kinds of checks against every *.oas.json file in your
project's config/ directory and against config/policies.json:
- OpenAPI quality — every operation has an
operationId, a description, and tags; path parameters are declared; no two paths are ambiguous. - Zuplo route configuration — every policy a route references exists in
policies.json,x-zuplo-routeblocks contain only valid properties, and routes have a label. The stricter presets also require authentication and rate limiting on every route, andzuplo:strictrequires a CORS setting. - Policy configuration —
policies.jsonhas the right structure, and each built-in policy'shandler.optionsmatch that policy's schema, so a missingrequestsAllowedon a rate limit policy or a misspelled enum value shows up as a warning before you deploy.
Optionally, the linter also compares your OpenAPI files against a Git branch and reports breaking changes, such as a removed endpoint.
The full list of checks, with an example of each, is in the rules reference.
Run the linter
Run the command from the root of your Zuplo project — the directory that
contains config/:
Code
The linter prints each result grouped by file, followed by a summary:
Code
Each line shows the line:column of the problem, its severity, a message, the
ID of the rule that reported it, and the location inside the document. Use the
rule ID to look the rule up in the rules reference or to
change its severity in your configuration.
The header shows which presets ran, the severity that fails the run, and where
the configuration came from: default when there is no configuration file,
file (auto-detected) when the linter found a zuplo.lint.jsonc, or
file (--config) when you passed one.
When nothing is left to report, the run ends with No violations found. and
exits with 0.
To lint a project in another directory, pass --dir:
Code
Errors, warnings, and the exit code
Every rule reports at a severity of error or warn. By default, the command
fails only when it finds at least one error, so warnings point out improvements
without blocking you.
| Exit code | Meaning |
|---|---|
0 | No results at or above the failure threshold. |
1 | The linter found results at or above the failure threshold. |
2 | The linter couldn't run: the directory isn't a Zuplo project, or the configuration file is invalid. |
Change the threshold with --fail-on:
Code
To set the threshold for everyone on the project, set
failOn in the configuration file
instead. The --fail-on flag overrides it.
Fix problems automatically
Some problems have exactly one correct fix. Run --fix to apply those fixes to
your OpenAPI files, then lint the result:
Code
Code
The results printed after the fixes are the problems that still need your attention.
--fix repairs two rules:
| Rule | Fix |
|---|---|
operation-operationId | Adds an operationId built from the method and path. DELETE /todos/{id} becomes deleteTodosById. |
zuplo/no-empty-inbound-policies | Removes an empty policies.inbound array, and the policies object if nothing else is left in it. |
Everything else — descriptions, tags, labels, missing policies, path parameters, authentication — needs a decision only you can make, so the linter reports it and leaves the file alone.
Automatic fixes are safe to run repeatedly:
- Formatting is preserved. The linter edits only what it fixes (plus a
trailing comma when it removes a property), so the change shows up as a small
diff rather than a reformatted file. Files with nothing to fix aren't written.
If an OpenAPI file isn't valid JSON, the linter changes nothing and exits with
2. - Generated
operationIdvalues are unique across the whole project, not only within one file. If the name is already taken, the linter adds a number:getTodos2. - Your configuration is respected. A rule that's off for a route, through
rules,overrides, orignorePaths, isn't fixed on that route. - Running it twice changes nothing the second time.
Commit or stash your changes before running --fix, so the fixes show up as
their own diff and are easy to review.
Output formats
The default text output is for reading in a terminal. Two other formats are
for tools:
| Format | Use it for |
|---|---|
text | Reading results in a terminal. Set NO_COLOR=1 to turn off colors. |
json | Scripts and custom CI steps. Includes a summary, every result, breaking changes, and fixes. |
sarif | GitHub code scanning and other tools that read SARIF 2.1.0. |
Code
The json output has this shape:
Code
See Lint in CI/CD for workflows that use each format.
Configure the linter
With no configuration, the linter runs the zuplo:recommended preset and fails
on errors. To choose a stricter preset, change or scope rules, add OpenAPI and
OWASP rulesets, write your own rules, or enable breaking change detection, add a
zuplo.lint.jsonc file to the root of your project:
zuplo.lint.jsonc
See Lint configuration for every option, and run
npx zuplo lint --list-rules to see every rule you can use.
Next steps
- Lint configuration — presets, rule severities, required policies, and breaking change detection.
- Lint rules reference — what each rule checks and how to fix it.
- Custom lint rules — write your own rules and share them across projects.
- Breaking change detection — compare your API with a Git branch and catch changes that break consumers.
- Lint in CI/CD — fail pull requests on lint errors and show results in GitHub code scanning.
zuplo lintCLI reference — every flag the command accepts.