開發者

把一本書標成讀過,就是一個 API 操作。

人在藏書庫把作品標成「讀過」,網站送出的就是下面這個請求。你的程式送出相同的請求,會得到相同的結果,也受相同的檢查。

寫入

一個寫入請求,逐行看

PUT /v1/works/{id}/reader-status
Authorization: Bearer …
Idempotency-Key: 7f3c2a9e-read
Content-Type: application/json

{
  "actingSubject":
    "https://rezics.com/id/…",
  "expectedVersion": 3,
  "status": "read",
  "finishedOn": "2026-10-19"
}
版本號、日期與冪等鍵是示意值。
  • 路徑指向一部作品

    {id}

    REZICS 裡每樣東西都有一個以 https://rezics.com/id/ 開頭的固定識別碼,換語言、換站點都是同一個。

  • 寫明身分

    actingSubject

    一個帳號可以代表自己,也可以代表組織。每次寫入都要寫明以誰的身分行動,伺服器在執行前檢查這個身分的權限。

  • 讓重送安全

    Idempotency-Key

    每次寫入帶一把冪等鍵。逾時或斷線之後,用同一把鍵重送,不會多寫一次。

  • 防止覆蓋

    expectedVersion

    你讀到第 3 版,就寫明第 3 版。別人在這之間先改了,這次寫入不會蓋過去。

  • 錯誤是問題詳情

    application/problem+json

    失敗時,code 是固定的代碼,程式依它分支,不必比對訊息文字。

重送

重送同一個請求,拿回同一個結果。

同一把鍵、同樣的內容再送一次,伺服器回傳第一次的結果,版本仍是 4,replayed 變成 true。同一把鍵換了內容,回傳 409 reader_status_conflict;版本已經不是 3,回傳 409 stale_reader_status,重新讀取再寫。

  • 第一次

    200
    {
      "status": "read",
      "version": 4,
      "replayed": false
    }
  • 重送

    200
    {
      "status": "read",
      "version": 4,
      "replayed": true
    }
  • 版本過期

    409
    {
      "status": 409,
      "code":
        "stale_reader_status"
    }

型別

API 和型別,來自同一份定義

REZICS 的網站用 TypeScript 呼叫 API,請求與回應的型別直接取自伺服器的路由宣告。欄位改了名字,或多了一種錯誤代碼,呼叫端在編譯時就會知道。TypeScript SDK 由同一份定義產生,你拿到的型別和網站相同。

OpenAPI 3.1 文件也由同一份路由宣告產生,寫明每個操作的驗證方式、請求格式,以及每種狀態碼的回應。

main().v1
  .works({ id: uuid(work) })
  ['reader-status'].put({
    actingSubject,
    expectedVersion,
    status: 'read',
    ...dates,
  }, headers())
節錄自 REZICS 網站的藏書庫功能。

generated/openapi/main/public.json

讀取

讀取也有規矩

  • nextCursor · snapshot

    長清單附上下一頁的游標,一頁接一頁讀到底。匯出藏書庫另附快照,資料在中途改變時會明說,不會交給你拼湊的檔案。

  • count.precision

    搜尋結果的總數會寫明是精確值,還是「至少這麼多」。

  • title.language · basis

    標題附語言標籤與書寫方向,並說明這個語言是你要求的、同一種文字的、其他文字的,還是備用的。

  • 404 resource_unavailable

    你無權看見的資源,回應和不存在的資源一樣,不會透露它存在。

用同一套 API,寫自己的工具。

讓自己的程式讀寫作品、藏書庫與社群資料。每次操作,都沿用 REZICS 的身分與授權規則。