An API style guide may require a check that a linter cannot express with its built-in functions. A custom Spectral function supplies the validation code: a rule selects values in an OpenAPI definition, and the function checks each target value and reports problems. Before writing one, check for a built-in rule, an importable ruleset, or a rule using a core function; custom code adds maintenance work. section 10.1 section 10.1.1
Check GET operationId prefixes
Suppose an organization requires every GET operationId to start with “retrieve” or “download.” A Spectral rule can select $.paths[*].get.operationId, set error severity, and call getOperationIdConvention. The function receives the selected string and returns a diagnostic when its prefix is wrong. This naming check can also be expressed with Spectral’s core pattern function, so it does not, by itself, require custom code. section 10.1.1 section 10.1.3 section 10.1.4
When bespoke code is needed:
- Start with a failing unit test for the desired rule behavior. Define the rule’s selection and diagnostic settings, then connect it to the function. section 10.1 section 10.1.3
- In TypeScript, createRulesetFunction can declare a string input, reject invalid input, and return an array of diagnostic results. Test both valid and invalid values; the demonstrated npm test run passes both cases. section 10.1.4
- Keep checks readable and deterministic. Linting may run in an editor, CLI, or browser, so weigh side effects, external libraries, HTTP authentication, and browser restrictions before depending on them. Roll out new rules gradually across existing APIs. section 10.1.5
Listing 10.3 Custom rule to check operationId prefix
export default {
rules: {
'operationId-get-should-follow-naming-convention': {
description: 'GET operations should have an operationId
starting with retrieve or download',
message: "GET operationId should follow
naming convention. {{error}}",
given: "$.paths[*].get.operationId", #1
then: {
function: getOperationIdConvention #2
},
severity: DiagnosticSeverity.Error,
},
The rule listing shows the JSONPath selection, custom-function call, and error severity together. section 10.1.3
Listing 10.4 Defining the custom function in TypeScript
import {createRulesetFunction} from "@stoplight/spectral-core"
import type {IFunctionResult, RulesetFunctionContext}
from '@stoplight/spectral-core';
type Input = string;
type Options = null;
export default createRulesetFunction<Input, Options>({
input: { #1
type: 'string'
},
errorOnInvalidInput: true, #2
options: null, #3
}, function operationIdConvention(targetValue, #4
opts, #5
context) { #6
const results: IFunctionResult[] = [];
if (!(targetValue.startsWith("retrieve")
|| targetValue.startsWith("download"))) {
results.push({ #7
message: `OperationId '${targetValue}' should start
with 'retrieve' or 'download'.`,
path: ['paths', '/v1/users', 'get', 'operationId'],
});
}
return results;
}
);
The function listing shows the declared input and the diagnostic produced for a noncompliant prefix. section 10.1.4
