Spectralを利用して、OpenAPI, SwaggerのコードをLintする
Spectralは、
OpenAPI(v2, v3)やAsyncAPIのlinterです。
Custom Ruleの生成が比較的簡単にできる点、swagger-uiを利用している場合にinvalidケースなどに気づきにくいことが多々あり、利用するメリットがあるかと思います。
Typespecなどを利用している時に、噛み合わせが悪いコードをlintで制約を追加したりしてバグを予防したりすることができます。
0. Before do
0-0. Project Structure
想定しているディレクトリ構成とファイルの中身
tree . -I node_modules
.
├── README.md
├── compose.yaml
├── dockerfile
├── openapi3
│ ├── generated
│ │ └── @typespec
│ │ └── openapi3
│ │ └── openapi.yaml
├── package-lock.json
├── package.json
compose.yaml
compose.yaml
services:
http_schema_container:
container_name: http_schema_container
build:
context: .
dockerfile: dockerfile
tty: true
restart: always
volumes:
- .:/usr/src/app
dockerfile
FROM node:23-alpine3.19
WORKDIR /usr/src/app
openapi3/generated/@typespec/openapi3/openapi.yaml
openapi3/generated/@typespec/openapi3/openapi.yaml
openapi: 3.0.0
info:
title: (title)
version: 0.0.0
tags: []
paths:
/pets:
get:
operationId: Pets_list
parameters:
- name: filter
in: query
required: true
schema:
type: string
explode: false
responses:
'200':
description: The request has succeeded.
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Pet'
post:
operationId: Pets_create
parameters: []
responses:
'200':
description: The request has succeeded.
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
/pets/{id}:
get:
operationId: Pets_read
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
'200':
description: The request has succeeded.
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
/stores:
get:
operationId: Stores_list
parameters:
- name: filter
in: query
required: true
schema:
type: string
explode: false
responses:
'200':
description: The request has succeeded.
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Store'
/stores/{id}:
get:
operationId: Stores_read
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
'200':
description: The request has succeeded.
content:
application/json:
schema:
$ref: '#/components/schemas/Store'
components:
schemas:
Address:
type: object
required:
- street
- city
properties:
street:
type: string
city:
type: string
Pet:
type: object
required:
- name
- age
properties:
name:
type: string
age:
type: integer
format: int32
Store:
type: object
required:
- name
- address
properties:
name:
type: string
address:
$ref: '#/components/schemas/Address'
0-1. typespec を利用して、OpenAPIのコードを生成する
tree . -I node_modules
.
├── README.md
├── compose.yaml
├── dockerfile
├── openapi3
│ ├── generated
│ │ └── @typespec
│ │ └── openapi3
│ │ └── openapi.yaml
│ ├── main.tsp
│ └── tspconfig.yaml
├── package-lock.json
├── package.json
openapi3/main.tsp
openapi3/main.tsp
import "@typespec/http";
using TypeSpec.Http;
model Pet {
name: string;
age: int32;
}
model Store {
name: string;
address: Address;
}
model Address {
street: string;
city: string;
}
@route("/pets")
interface Pets {
list(@query filter: string): Pet[];
create(@body pet: Pet): Pet;
read(@path id: string): Pet;
}
@route("/stores")
interface Stores {
list(@query filter: string): Store[];
read(@path id: string): Store;
}
openapi3/tspconfig.yaml
openapi3/tspconfig.yaml
warn-as-error: true # Treat warnings as errors
output-dir: "{cwd}/openapi3/generated" # Configure the base output directory for all emitters
emit:
- "@typespec/openapi3"
1. Spectralの依存関係を設定する
% npm install @stoplight/spectral-cli
% echo 'extends: ["spectral:oas"]' > .spectral.yaml
package.json
"scripts": {
"cli:spectral": "spectral"
}
tree . -I node_modules
.
├── .spectral.yaml
├── README.md
├── compose.yaml
├── dockerfile
├── openapi3
│ ├── generated
│ │ └── @typespec
│ │ └── openapi3
│ │ └── openapi.yaml
├── package-lock.json
├── package.json
2. SpectralのLintを実施する
package.json
"scripts": {
"lint:openapi": "spectral lint ./openapi3/generated/@typespec/openapi3/openapi.yaml"
}
% npm run lint:openapi
> app@0.1.0 lint:openapi
> spectral lint ./openapi3/generated/@typespec/openapi3/openapi.yaml
/usr/src/app/openapi3/generated/@typespec/openapi3/openapi.yaml
1:1 warning oas3-api-servers OpenAPI "servers" must be present and non-empty array.
2:6 warning info-contact Info object must have "contact" object. info
2:6 warning info-description Info "description" must be present and non-empty string. info
8:9 warning operation-description Operation "description" must be present and non-empty string. paths./pets.get
8:9 warning operation-tags Operation must have non-empty "tags" array. paths./pets.get
26:10 warning operation-description Operation "description" must be present and non-empty string. paths./pets.post
26:10 warning operation-tags Operation must have non-empty "tags" array. paths./pets.post
43:9 warning operation-description Operation "description" must be present and non-empty string. paths./pets/{id}.get
43:9 warning operation-tags Operation must have non-empty "tags" array. paths./pets/{id}.get
59:9 warning operation-description Operation "description" must be present and non-empty string. paths./stores.get
59:9 warning operation-tags Operation must have non-empty "tags" array. paths./stores.get
78:9 warning operation-description Operation "description" must be present and non-empty string. paths./stores/{id}.get
78:9 warning operation-tags Operation must have non-empty "tags" array. paths./stores/{id}.get
✖ 13 problems (0 errors, 13 warnings, 0 infos, 0 hints)