iT邦幫忙

0

Day10.用 Swagger Editor 建第一份 OpenAPI(核心版)

  • 分享至 

  • xImage
  •  

目標

  1. 把 /bus/routes、/bus/stops、/bus/eta 寫成一份最小可用的 OpenAPI(YAML)。
  2. 只放一定會用到的核心欄位;先能讀懂、能產生文件,再慢慢加細節。
    原則
    • 欄位命名:小駝峰(routeId, stopId, estimateSeconds, updateTime)。
    • URL:kebab-case(/bus/routes, /bus/stops, /bus/eta)。
    • 狀態字:可讀字串(stopStatus = normal | last | suspended | noData)。
    • 時間:ISO 8601+時區(例 2025-10-20T08:29:00+08:00)。
    • 對齊:routeId + stopId(必要時加 direction)。
    openapi.yaml(核心版,可直接貼到 Swagger Editor)
    openapi: 3.0.3
    info:
    title: Taipei Bus Mini API (Core)
    version: 0.1.0
    description: |
    三支端點:/bus/routes、/bus/stops、/bus/eta。
    命名:JSON 小駝峰;URL kebab-case;時間 ISO 8601(含時區);
    倒數用 estimateSeconds(整數、秒);對齊以 routeId + stopId(+ direction)。

servers:

tags:

  • name: bus
    description: 路線、站點與到站資訊

paths:
/bus/routes:
get:
tags: [bus]
summary: 取得路線清單(固定)
parameters:
- $ref: '#/components/parameters/city'
- $ref: '#/components/parameters/keyword'
- $ref: '#/components/parameters/page'
- $ref: '#/components/parameters/pageSize'
responses:
'200':
description: 路線清單
content:
application/json:
schema:
$ref: '#/components/schemas/RoutesResponse'
'400':
$ref: '#/components/responses/BadRequest'
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
$ref: '#/components/responses/ServerError'

/bus/stops:
get:
tags: [bus]
summary: 取得站點清單(固定)
parameters:
- $ref: '#/components/parameters/routeId'
- $ref: '#/components/parameters/direction'
- $ref: '#/components/parameters/city'
responses:
'200':
description: 站點清單
content:
application/json:
schema:
$ref: '#/components/schemas/StopsResponse'
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
$ref: '#/components/responses/ServerError'

/bus/eta:
get:
tags: [bus]
summary: 取得到站預估(即時)
parameters:
- $ref: '#/components/parameters/routeId'
- $ref: '#/components/parameters/stopId'
- $ref: '#/components/parameters/directionOptional'
responses:
'200':
description: 到站預估
content:
application/json:
schema:
$ref: '#/components/schemas/EtaResponse'
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
$ref: '#/components/responses/ServerError'

components:
parameters:
city:
name: city
in: query
required: false
schema:
type: string
description: 城市或範圍(若來源需要)
keyword:
name: keyword
in: query
required: false
schema:
type: string
description: 以路線名稱關鍵字過濾
page:
name: page
in: query
required: false
schema:
type: integer
minimum: 1
pageSize:
name: pageSize
in: query
required: false
schema:
type: integer
minimum: 1
maximum: 200
routeId:
name: routeId
in: query
required: true
schema:
type: string
description: 路線識別(字串)
stopId:
name: stopId
in: query
required: true
schema:
type: string
description: 站點識別(字串)
direction:
name: direction
in: query
required: true
schema:
type: integer
enum: [0, 1]
description: 0=去程/1=返程
directionOptional:
name: direction
in: query
required: false
schema:
type: integer
enum: [0, 1]
description: 若雙向共用 stopId,建議帶上

schemas:
LocalizedName:
type: object
properties:
zh:
type: string
en:
type: string
additionalProperties: false

RouteItem:
  type: object
  properties:
    routeId:
      type: string
    routeName:
      $ref: '#/components/schemas/LocalizedName'
    hasWheelchair:
      type: boolean
    updateTime:
      type: string
      format: date-time
      description: 'ISO 8601(含時區)'
  required: [routeId, routeName, updateTime]
  additionalProperties: false

RoutesResponse:
  type: object
  properties:
    routes:
      type: array
      items:
        $ref: '#/components/schemas/RouteItem'
  required: [routes]
  additionalProperties: false

StopItem:
  type: object
  properties:
    stopId:
      type: string
    stopName:
      $ref: '#/components/schemas/LocalizedName'
    lat:
      type: number
    lon:
      type: number
    sequence:
      type: integer
      minimum: 1
  required: [stopId, stopName]
  additionalProperties: false

StopsResponse:
  type: object
  properties:
    routeId:
      type: string
    direction:
      type: integer
      enum: [0, 1]
    stops:
      type: array
      items:
        $ref: '#/components/schemas/StopItem'
    updateTime:
      type: string
      format: date-time
      description: 'ISO 8601(含時區)'
  required: [routeId, direction, stops, updateTime]
  additionalProperties: false

EtaResponse:
  type: object
  properties:
    routeId:
      type: string
    stopId:
      type: string
    direction:
      type: integer
      enum: [0, 1]
    estimateSeconds:
      type: integer
      minimum: 0
      description: '倒數秒數(顯示層再轉成人話)'
    stopStatus:
      type: string
      enum: [normal, last, suspended, noData]
    updateTime:
      type: string
      format: date-time
      description: 'ISO 8601(含時區)'
  required: [routeId, stopId, estimateSeconds, stopStatus, updateTime]
  additionalProperties: false

ErrorResponse:
  type: object
  properties:
    code:
      type: string
      description: '短代碼(英文小寫+底線)'
    message:
      type: string
      description: '人看得懂的一句話'
    status:
      type: integer
      description: 'HTTP 狀態碼'
    timestamp:
      type: string
      format: date-time
      description: 'ISO 8601(含時區)'
    traceId:
      type: string
      description: '追蹤用識別碼'
    userHint:
      type: string
      description: '下一步建議'
    details:
      type: object
      additionalProperties: true
  required: [code, message, status, timestamp]
  additionalProperties: false

responses:
BadRequest:
description: 參數錯誤
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
NotFound:
description: 查無資料
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
TooManyRequests:
description: 查詢過於頻繁
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
ServerError:
description: 服務端錯誤
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'


圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言