Site icon 134340號小行星

如何設計更好的 API?關於 REST API 設計的 15 個技巧

1s3bedypkt7zm8maikzg 如何設計更好的 API?關於 REST API 設計的 15 個技巧

本篇文章中提到的技巧摘錄自 How to design better APIs

原文中作者提出了 15 點 設計 REST API 的技巧,大部分是我認為非常有必要且重要的,因此引用了一部分原文中的內容並做了一些補充分享給大家。但這些技巧並不是硬性「規定」,可以根據團隊風格或者專案情況…等自行決定。

如果對 REST API 是什麼還並不了解的話,引用 淺談 REST API 的設計和規劃 – 大類的技術筆記 提到的這段簡單做個解釋:

這篇文章提到非常多關於 REST API 的設計與規劃技巧,受益良多,有興趣可以看看

保持一致

原文中提到了以下六點:

補充

另外根據我工作上看到的例子,有幾點想補充:

關於 REST API 的命名規範網路上有很多可以參考的文章,這邊就不延伸下去了。

使用 ISO 8601 UTC 標準處理時間

在處理日期和時間時,API 應始終返回 ISO 8601 標準的字串,並且日期應根據時區顯示。

{
    "published_at": "2022-03-03T21:59:08Z"
}

補充

JS 的 Date 有提供 toISOString() API 來將時間轉為 ISO 8601 標準的字串:

const event = new Date();
console.log(event.toString());
// Sun Apr 24 2022 16:19:01 GMT+0800 (台北標準時間)

console.log(event.toISOString());
// 2022-04-24T08:19:01.895Z

如果你的 project 使用的是 moment.js 來處理時間,也一樣可以 toISOString() API 來將時間轉為 ISO 8601 標準的字串:

const time = moment().toISOString();
console.log(time); // 2022-04-24T08:19:01.895Z

要注意的是,如果使用 toISOString() 返回的都是 UTC 時間。

對公共接口進行例外處理

(此處的接口指的是 endpoints,也就是 GET /employee/{id}, GET /client/detail…等)

在默認情況下,每個接口都應該需要鑒權。但有些情況比如說:登入、註冊,是不需要鑒權的,就需要將它們設置成允許未經授權。

提供健康檢查接口

提供一個 GET /health 的接口用於判斷目前 API 服務是否正常運行。其他應用程序,例如負載平衡器(load balancers)可以調用此接口以在服務中斷時採取行動。

比如說:

app.get('/health', (req, res) => {
    res.setHeader('Content-Type', 'application/json');
    res.send({ "message":  "I'm still alive." });
});

版本化 API

確保對 API 進行版本控制並在每個請求中傳遞版本。API 版本可以使用 HTTP Headers 或查詢/路徑參數傳遞。即使是 API 的第一個版本(1.0) 也應該明確地進行版本控制。

一些例子:

接受 API 密鑰認證

如果 API 需要由第三方調用時,則允許通過 API keys 來進行身分驗證。API keys 應使用自定義 HTTP Headers 傳遞,並且應該有一個有效日期,且可以隨時撤銷。同時應避免將 API keys 嵌入程式碼中,可以使用環境變數來代替(例如: process.env)。

使用合理的 HTTP 狀態碼

使用傳統的 HTTP 狀態代碼來指示請求的成功或失敗,並在整個 API 中為相同的結果使用相同的狀態代碼。

舉一些例子:

使用合理的 HTTP 方法

HTTP 方法有很多,但最重要的是:

使用簡單且不言自明的名稱

大部分接口應面向資源命名,不要添加不必要的訊息。這同時也適用於 fields 的名稱。

✅好的例子如下:

❌糟糕的例子如下:

使用標準化的錯誤響應

除了指示請求結果(成功或失敗)的HTTP狀態碼之外,在返回錯誤時,應使用標準化的錯誤響應,才能得知確切的錯誤信息從而找出錯誤原因。

// Request => GET /users/4TL011ax
// Response <= 404 Not Found
{
    "code": "user/not_found",
    "message": "A user with the ID 4TL011ax could not be found."
}

// Request => POST /users
{
    "name": "John Doe"
}
// Response <= 400 Bad Request
{
    "code": "user/email_required",
    "message": "The parameter [email] is required."
}

POST 後返回創建的資源

POST 請求創建資源後返回創建的資源信息是非常有用的一步驟,因為有些情況下接下來的步驟會根據創建後的這個資源進行處理,所以會需要創建的資源的 id 或者其他信息。

// Request: POST /users
{
    "email": "jdoe@averagecompany.com",
    "name": "John Doe"
}

// Response
{
    "id": 1,
    "email": "jdoe@averagecompany.com",
    "name": "John Doe"
}

以 PATCH 取代 PUT

PATCHPUT 皆用於更新資源,但 PATCH 是對資源部分更新,而 PUT 是完全替換現有資源。

補充

舉個例子,假設今天有一個使用者資訊如下:

{
    id: 1
    first_name: 'John',
    last_name: 'Dog',
    age: 20
}

若我只是想修改 last_name 時,使用 PUT 我需要將所有字段傳回去:

{
    id: 1
    first_name: 'John',
    last_name: 'Doe',
    age: 20
}

而如果使用 PATCH,我只需要回傳 last_name 就可以進行更新:

{
    last_name: 'Doe',
}

響應結果過多時使用分頁

當響應結果過多時可以適當使用分頁。比如說以 page_numberpage_size …等來控制要獲取的結果內容。

// Request => GET /users?page_number=1&page_size=15

// Response <= 200 OK
{
    "page_number": 1,
    "page_size": 15,
    "count": 378,
    "data": [
        // resources
    ],
    "total_pages": 26,
    "has_previous_page": true,
    "has_next_page": true
}

補充

我比較常遇到的例子是用 offsetlimit,offset 用於指定從哪開始,limit 用於指定一次響應多少個結果。

例如:

http://localhost:8080/api/clients/list?offset=1&limit=10

代表從第一個結果開始返回十個結果。

Exit mobile version