FastDOL · API Governance Rules

FastDOL API Rules

Spectral linting rules defining API design standards and conventions for FastDOL.

32 Rules error 8 warn 18 info 6
View Rules File View on GitHub

Rule Categories

info no openapi operation parameter paths request response schema security servers

Rules

error
info-title-fastdol-prefix
API title must start with "FastDOL".
$.info.title
warn
info-description-required
info.description is required and must be at least 40 characters.
$.info
error
info-version-required
info.version is required.
$.info
warn
info-contact-email
info.contact.email should be present.
$.info
info
info-terms-of-service
info.termsOfService should be present.
$.info
error
openapi-version-3
OpenAPI version must be 3.0.x or 3.1.x.
$.openapi
error
servers-required
At least one server must be defined.
$
error
servers-https-only
All server URLs must use HTTPS.
$.servers[*].url
warn
servers-fastdol-host
Production server URL should be api.fastdol.com.
$.servers[*].url
warn
paths-version-prefix
Public data paths must be prefixed with /v1/ (auth, dashboard, webhooks excepted).
$.paths[?([email protected](/^\/(auth|dashboard|webhooks)\b/))]~
warn
paths-kebab-case
Path segments must be lower-case kebab-case (snake_case forbidden).
$.paths[*]~
error
paths-no-trailing-slash
Path must not end with a trailing slash.
$.paths[*]~
error
operation-operationid-required
Every operation must declare an operationId.
$.paths[*][get,post,put,delete,patch]
warn
operation-operationid-snake-case
operationId must be snake_case (FastAPI default).
$.paths[*][get,post,put,delete,patch].operationId
warn
operation-summary-required
Every operation must declare a summary.
$.paths[*][get,post,put,delete,patch]
warn
operation-summary-fastdol-prefix
Operation summary must be Title Case and prefixed with "FastDOL".
$.paths[*][get,post,put,delete,patch].summary
warn
operation-tags-required
Every operation should be tagged.
$.paths[*][get,post,put,delete,patch]
info
operation-microcks-extension
Operation should declare x-microcks-operation for mock-server compatibility.
$.paths[*][get,post,put,delete,patch]
warn
parameter-description-required
Every parameter must declare a description.
$.paths[*][get,post,put,delete,patch].parameters[*]
warn
parameter-snake-case
Parameter names must be snake_case.
$.paths[*][get,post,put,delete,patch].parameters[?(@.in!='header')]
warn
parameter-pagination-offset-limit
Pagination must use the offset/limit pair (no page/per_page).
$.paths[*][get,post,put,delete,patch].parameters[*].name
warn
request-body-json-content
Request bodies must offer application/json (multipart for upload only).
$.paths[*][post,put,patch].requestBody.content
error
response-success-required
Every operation must declare a 2xx success response.
$.paths[*][get,post,put,delete,patch].responses
warn
response-401-on-protected
Operations behind auth (X-Api-Key) should declare a 401 response.
$.paths[?([email protected](/^\/(auth|webhooks|v1\/health|v1\/sitemap)\b/))][get,post,put,delete,patch].responses
info
response-429-rate-limit
Data endpoints should declare a 429 rate-limit response.
$.paths[?(@property.match(/^\/v1\//))][get,post].responses
info
response-422-validation
Operations with parameters should declare a 422 validation response.
$.paths[*][get,post,put,delete,patch].responses
warn
response-json-content
Responses must use application/json.
$.paths[*][get,post,put,delete,patch].responses[*].content
warn
schema-property-snake-case
Schema properties must be snake_case.
$.components.schemas[*].properties[*]~
info
schema-id-property-naming
Identifier properties must end in _id (e.g. employer_id, job_id, key_id).
$.components.schemas[*].properties[?(@property.match(/(Id|ID|^id)$/))]~
warn
security-x-api-key-header
Auth must be the X-Api-Key header (FastDOL convention).
$.components.securitySchemes[*]
warn
no-empty-description
Descriptions must not be empty strings.
$..description
info
operation-examples-encouraged
Responses are encouraged to include named examples (Microcks-compatible).
$.paths[*][get,post,put,delete,patch].responses[*].content[*]

Spectral Ruleset

Raw ↑
# FastDOL Spectral Ruleset
# Enforces the conventions observed in the FastDOL OpenAPI specification
# (FastAPI-generated, snake_case paths and properties, X-Api-Key header auth, /v1 prefix).
# Apply with: spectral lint -r rules/fastdol-rules.yml openapi/fastdol-openapi.yml

extends: spectral:oas

rules:
  # ============================================================
  # INFO / METADATA
  # ============================================================
  info-title-fastdol-prefix:
    description: API title must start with "FastDOL".
    message: '{{property}} must start with "FastDOL".'
    severity: error
    given: $.info.title
    then:
      function: pattern
      functionOptions:
        match: '^FastDOL'

  info-description-required:
    description: info.description is required and must be at least 40 characters.
    message: '{{property}} must be present and meaningful.'
    severity: warn
    given: $.info
    then:
      - field: description
        function: truthy
      - field: description
        function: length
        functionOptions:
          min: 40

  info-version-required:
    description: info.version is required.
    severity: error
    given: $.info
    then:
      field: version
      function: truthy

  info-contact-email:
    description: info.contact.email should be present.
    severity: warn
    given: $.info
    then:
      field: contact.email
      function: truthy

  info-terms-of-service:
    description: info.termsOfService should be present.
    severity: info
    given: $.info
    then:
      field: termsOfService
      function: truthy

  # ============================================================
  # OPENAPI VERSION
  # ============================================================
  openapi-version-3:
    description: OpenAPI version must be 3.0.x or 3.1.x.
    severity: error
    given: $.openapi
    then:
      function: pattern
      functionOptions:
        match: '^3\.[01]\.\d+$'

  # ============================================================
  # SERVERS
  # ============================================================
  servers-required:
    description: At least one server must be defined.
    severity: error
    given: $
    then:
      field: servers
      function: truthy

  servers-https-only:
    description: All server URLs must use HTTPS.
    severity: error
    given: $.servers[*].url
    then:
      function: pattern
      functionOptions:
        match: '^https://'

  servers-fastdol-host:
    description: Production server URL should be api.fastdol.com.
    severity: warn
    given: $.servers[*].url
    then:
      function: pattern
      functionOptions:
        match: 'fastdol\.com'

  # ============================================================
  # PATHS — NAMING CONVENTIONS
  # ============================================================
  paths-version-prefix:
    description: 'Public data paths must be prefixed with /v1/ (auth, dashboard, webhooks excepted).'
    severity: warn
    given: $.paths[?([email protected](/^\/(auth|dashboard|webhooks)\b/))]~
    then:
      function: pattern
      functionOptions:
        match: '^/v1/'

  paths-kebab-case:
    description: Path segments must be lower-case kebab-case (snake_case forbidden).
    severity: warn
    given: $.paths[*]~
    then:
      function: pattern
      functionOptions:
        match: '^(/[a-z0-9-]+|/\{[a-z_][a-z0-9_]*\})+$'

  paths-no-trailing-slash:
    description: Path must not end with a trailing slash.
    severity: error
    given: $.paths[*]~
    then:
      function: pattern
      functionOptions:
        notMatch: '.+/$'

  # ============================================================
  # OPERATIONS
  # ============================================================
  operation-operationid-required:
    description: Every operation must declare an operationId.
    severity: error
    given: $.paths[*][get,post,put,delete,patch]
    then:
      field: operationId
      function: truthy

  operation-operationid-snake-case:
    description: operationId must be snake_case (FastAPI default).
    severity: warn
    given: $.paths[*][get,post,put,delete,patch].operationId
    then:
      function: pattern
      functionOptions:
        match: '^[a-z][a-z0-9_]*$'

  operation-summary-required:
    description: Every operation must declare a summary.
    severity: warn
    given: $.paths[*][get,post,put,delete,patch]
    then:
      field: summary
      function: truthy

  operation-summary-fastdol-prefix:
    description: Operation summary must be Title Case and prefixed with "FastDOL".
    severity: warn
    given: $.paths[*][get,post,put,delete,patch].summary
    then:
      function: pattern
      functionOptions:
        match: '^FastDOL\s'

  operation-tags-required:
    description: Every operation should be tagged.
    severity: warn
    given: $.paths[*][get,post,put,delete,patch]
    then:
      field: tags
      function: truthy

  operation-microcks-extension:
    description: Operation should declare x-microcks-operation for mock-server compatibility.
    severity: info
    given: $.paths[*][get,post,put,delete,patch]
    then:
      field: x-microcks-operation
      function: truthy

  # ============================================================
  # PARAMETERS
  # ============================================================
  parameter-description-required:
    description: Every parameter must declare a description.
    severity: warn
    given: $.paths[*][get,post,put,delete,patch].parameters[*]
    then:
      field: description
      function: truthy

  parameter-snake-case:
    description: Parameter names must be snake_case.
    severity: warn
    given: $.paths[*][get,post,put,delete,patch].parameters[?(@.in!='header')]
    then:
      field: name
      function: pattern
      functionOptions:
        match: '^[a-z][a-z0-9_]*$'

  parameter-pagination-offset-limit:
    description: Pagination must use the offset/limit pair (no page/per_page).
    severity: warn
    given: $.paths[*][get,post,put,delete,patch].parameters[*].name
    then:
      function: pattern
      functionOptions:
        notMatch: '^(page|per_page|page_size|pageSize|cursor)$'

  # ============================================================
  # REQUEST BODIES
  # ============================================================
  request-body-json-content:
    description: Request bodies must offer application/json (multipart for upload only).
    severity: warn
    given: $.paths[*][post,put,patch].requestBody.content
    then:
      function: schema
      functionOptions:
        schema:
          anyOf:
            - required: ['application/json']
            - required: ['multipart/form-data']

  # ============================================================
  # RESPONSES
  # ============================================================
  response-success-required:
    description: Every operation must declare a 2xx success response.
    severity: error
    given: $.paths[*][get,post,put,delete,patch].responses
    then:
      function: schema
      functionOptions:
        schema:
          anyOf:
            - required: ['200']
            - required: ['201']
            - required: ['202']
            - required: ['204']

  response-401-on-protected:
    description: Operations behind auth (X-Api-Key) should declare a 401 response.
    severity: warn
    given: "$.paths[?([email protected](/^\\/(auth|webhooks|v1\\/health|v1\\/sitemap)\\b/))][get,post,put,delete,patch].responses"
    then:
      field: '401'
      function: truthy

  response-429-rate-limit:
    description: Data endpoints should declare a 429 rate-limit response.
    severity: info
    given: $.paths[?(@property.match(/^\/v1\//))][get,post].responses
    then:
      field: '429'
      function: truthy

  response-422-validation:
    description: Operations with parameters should declare a 422 validation response.
    severity: info
    given: $.paths[*][get,post,put,delete,patch].responses
    then:
      field: '422'
      function: truthy

  response-json-content:
    description: Responses must use application/json.
    severity: warn
    given: $.paths[*][get,post,put,delete,patch].responses[*].content
    then:
      function: schema
      functionOptions:
        schema:
          anyOf:
            - required: ['application/json']
            - required: ['text/csv']
            - required: ['application/pdf']

  # ============================================================
  # SCHEMAS — PROPERTY NAMING
  # ============================================================
  schema-property-snake-case:
    description: Schema properties must be snake_case.
    severity: warn
    given: $.components.schemas[*].properties[*]~
    then:
      function: pattern
      functionOptions:
        match: '^[a-z][a-z0-9_]*$'

  schema-id-property-naming:
    description: Identifier properties must end in _id (e.g. employer_id, job_id, key_id).
    severity: info
    given: "$.components.schemas[*].properties[?(@property.match(/(Id|ID|^id)$/))]~"
    then:
      function: pattern
      functionOptions:
        match: '_id$'

  # ============================================================
  # SECURITY
  # ============================================================
  security-x-api-key-header:
    description: Auth must be the X-Api-Key header (FastDOL convention).
    severity: warn
    given: $.components.securitySchemes[*]
    then:
      function: schema
      functionOptions:
        schema:
          type: object
          properties:
            type: { const: 'apiKey' }
            in: { const: 'header' }
            name: { const: 'X-Api-Key' }

  # ============================================================
  # GENERAL QUALITY
  # ============================================================
  no-empty-description:
    description: Descriptions must not be empty strings.
    severity: warn
    given: "$..description"
    then:
      function: truthy

  operation-examples-encouraged:
    description: Responses are encouraged to include named examples (Microcks-compatible).
    severity: info
    given: $.paths[*][get,post,put,delete,patch].responses[*].content[*]
    then:
      function: schema
      functionOptions:
        schema:
          anyOf:
            - required: ['example']
            - required: ['examples']