Skip to content

Commit b723966

Browse files
authored
Web API設計ガイドライン: null値の扱いについて追記 (#18)
1 parent 2cf78d6 commit b723966

File tree

1 file changed

+68
-38
lines changed

1 file changed

+68
-38
lines changed

documents/forWebAPI/web_api_guidelines.md

Lines changed: 68 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,15 @@ Web APIのサーバサイドの設計についてまとめる。クライアン
4848

4949
各要素ごとのWeb APIの表記は以下のルールとする。表記形式を統一することで設計時の考慮事項や、Web API利用時のミスを減らすことを目的とする。
5050

51-
| 対象 | 推奨 || 理由 |
52-
| :------------------------- | :------------------ | :----------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
53-
| ホスト名 | kebab-case | api.example.com | ホスト名にはアンダースコア(\_)の使用をRFC 952、RFC 1123では許容していない。RFC1123では大文字と小文字を区別しなくても良いとあるため。必然的に文字の区切りはハイフンであるkebab-caseを利用する |
54-
| パス(リソース名) | kebab-case \+複数形 | delivery-schedules | ホスト名がkebab-caseであるため、それと整合性を取る。 また、REST志向という前提であるため、リソース名の最後は複数形にする |
55-
| リクエストヘッダ | kebab-case | x-debug-enabled | ※1 |
56-
| クエリパラメータ | snake_case | order_id | |
57-
| リクエストボディのJSON項目 | snake_case | order_id | ※2 |
58-
| レスポンスヘッダ | kebab-case | x-debug-logs | ※1 |
59-
| レスポンスボディのJSON項目 | snake_case | order_id | |
51+
| 対象 | 推奨 | | 理由 |
52+
| :------------------------- | :------------------ | :---------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
53+
| ホスト名 | kebab-case | api.foo-bar.example.com | ホスト名にはアンダースコア(\_)の使用をRFC 952、RFC 1123では許容していない。RFC1123では大文字と小文字を区別しなくても良いとあるため。必然的に文字の区切りはハイフンであるkebab-caseを利用する |
54+
| パス(リソース名) | kebab-case \+複数形 | delivery-schedules | ホスト名がkebab-caseであるため、それと整合性を取る。 また、REST志向という前提であるため、リソース名の最後は複数形にする |
55+
| リクエストヘッダ | kebab-case | x-debug-enabled | ※1 |
56+
| クエリパラメータ | snake_case | order_id | |
57+
| リクエストボディのJSON項目 | snake_case | order_id | ※2 |
58+
| レスポンスヘッダ | kebab-case | x-debug-logs | ※1 |
59+
| レスポンスボディのJSON項目 | snake_case | order_id | |
6060

6161
※1 ヘッダーフィールドについて
6262

@@ -232,10 +232,10 @@ Web APIのホスティング戦略とは、Web APIのエンドポイントをど
232232

233233
後方互換性のある改修のうち、パッチバージョンアップやマイナーバージョンアップと呼ばれるような改修には以下がある。
234234

235-
| 項目 | 改修例 |
236-
| :----------------------- | :------------------------------------------------------------------ |
237-
| パッチバージョンアップ | ・小さなバグ修正 ・エラーメッセージのtypo修正 ・APIドキュメント修正 |
238-
| マイナーバージョンアップ | ・新しいオプションが追加 ・性能改善、セキュリティ強化など |
235+
| 項目 | 改修例 |
236+
| :----------------------- | :-------------------------------------------------------------------------- |
237+
| パッチバージョンアップ | ・小さなバグ修正<br> ・エラーメッセージのtypo修正<br> ・APIドキュメント修正 |
238+
| マイナーバージョンアップ | ・新しいオプションが追加<br> ・性能改善、セキュリティ強化など |
239239

240240
本規約の推奨は以下の通り。
241241

@@ -511,10 +511,10 @@ Idempotency-Keyヘッダという[IETF Draft(2024年12月時点ではDraft 05
511511

512512
PATCHメソッドはリソースの部分更新をサポートするが、どのような形式にするかはRFC 5789で決まっていない。一般的には以下の形式が考えられる。
513513

514-
| 観点 | JSON Patch | JSON Merge Patch★推奨 |
515-
| :------- | :----------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------ |
516-
| 説明 | RFC 6902。操作が明確に定義でき、細かい制御が得意 | RFC 7386。シンプルな構文で、元のJSONに対して新しい値を上書きするだけのため、直感的である。 |
517-
| 主な制約 | ・フォーマットがやや複雑で学習コストが必要 | ・配列のある要素だけ更新といった高度な操作はできない ・フィールドの削除をnullで表現するため、nullの値を持つことができる場合は、仕様通りにならない |
514+
| 観点 | JSON Patch | JSON Merge Patch★推奨 |
515+
| :------- | :----------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------- |
516+
| 説明 | RFC 6902。操作が明確に定義でき、細かい制御が得意 | RFC 7386。シンプルな構文で、元のJSONに対して新しい値を上書きするだけのため、直感的である。 |
517+
| 主な制約 | ・フォーマットがやや複雑で学習コストが必要 | ・配列のある要素だけ更新といった高度な操作はできない<br> ・フィールドの削除をnullで表現するため、nullの値を持つことができる場合、表現が難しい |
518518

519519
以下に `PATCH /users/123` する場合に以下の操作を行った場合のリクエスト例を上げる。
520520

@@ -558,11 +558,28 @@ JSON Merge Patch:
558558

559559
- PATCHのリクエストボディはJSON Merge Patch形式に従う
560560
- JSON Merge Patchでハマりやすい、部分更新が出てきた場合は、PUTで処理できるエンドポイント設けるなど、PATCHにこだわりすぎない
561-
- `null` が指定された場合、バックエンドがPostgreSQLなどRDBの場合はnull値に更新し、項目の削除はしてもしなくてもどちらでも良い。以降のレスポンスは項目ごと送信しないか、null値で送信するかは、これとは別の方針で決定する
561+
- 本規約では、[#nullの扱い](#nullの扱い)の章の通り値が存在しないことを `undefined` で表現し `null` を利用しない。そのため、PATCHで項目削除のために `null` を利用することは問題ない
562562
- `Content-Type: application/merge-patch+json` で送信する。フレームワークなどの都合で対応できない場合は、 `Content-Type: application/json` も許容する
563563

564564
参考: [Web API 設計のベスト プラクティス \- Azure Architecture Center](https://learn.microsoft.com/ja-jp/azure/architecture/best-practices/api-design#patch-methods)
565565

566+
# リクエストヘッダ
567+
568+
下表にてリクエストヘッダーについて推奨をまとめる。
569+
570+
| 項目 | 推奨 |
571+
| :-------------- | :---------------------------------------------------------------------------------------------------------------- |
572+
| Authorization | 認証トークンなどは Authorizationヘッダ(またはCookie)に設定する。クエリパラメータやボディには設定しない |
573+
| Content-Type | APIが対応しているメディアタイプを指定する(多くは `application/json` になる想定) |
574+
| User-Agent | クライアントが対向システムである場合、利用実績の把握の把握のため `User-Agent: SystemABC/1.0` などと指定しても良い |
575+
| Accept-Language | 多言語対応しているWeb APIの場合は指定可能にする |
576+
577+
カスタムヘッダーについて、本規約の推奨は以下の通り。
578+
579+
- カスタムヘッダを追加する際は、その用途を明確化する
580+
- カスタムヘッダが増えることで、Web API仕様が複雑になりがちである。そのため、カスタムヘッダは必要最小限の追加とする
581+
- 命名は「スタイル」章を参照
582+
566583
# クエリパラメータ
567584

568585
クエリーパラメータは以下のURLの `key1=value1&key2=value2` にあたる要素を指す。
@@ -695,22 +712,11 @@ Web APIのクエリパラメータで `fields` を使って必要な項目だけ
695712
- 開発工数が必要となるため、必要が無ければ導入しない
696713
- 導入する場合も、必要最低限の機能のみに追加する
697714

698-
# リクエストヘッダ
715+
# リクエストボディ
699716

700-
下表にてリクエストヘッダーについて推奨をまとめる。
717+
## nullの扱い
701718

702-
| 項目 | 推奨 |
703-
| :-------------- | :---------------------------------------------------------------------------------------------------------------- |
704-
| Authorization | 認証トークンなどは Authorizationヘッダ(またはCookie)に設定する。クエリパラメータやボディには設定しない |
705-
| Content-Type | APIが対応しているメディアタイプを指定する(多くは `application/json` になる想定) |
706-
| User-Agent | クライアントが対向システムである場合、利用実績の把握の把握のため `User-Agent: SystemABC/1.0` などと指定しても良い |
707-
| Accept-Language | 多言語対応しているWeb APIの場合は指定可能にする |
708-
709-
カスタムヘッダーについて、本規約の推奨は以下の通り。
710-
711-
- カスタムヘッダを追加する際は、その用途を明確化する
712-
- カスタムヘッダが増えることで、Web API仕様が複雑になりがちである。そのため、カスタムヘッダは必要最小限の追加とする
713-
- 命名は「スタイル」章を参照
719+
[#値が存在しないという状態の表現](#値が存在しないという状態の表現) の内容に従う。PATCHメソッドの場合は、[#PATCHによる部分更新](#PATCHによる部分更新) の内容に従う。
714720

715721
# HTTPステータスコード
716722

@@ -791,7 +797,7 @@ HTTPステータスコードをできる限り細かく使い分けることに
791797

792798
DBなどから取得したレコードをそのまま配列のままで返すのではなく、一律オブジェクトでラップして返すこととする。
793799

794-
`推奨: オブジェクトをトップレベルにする`
800+
✅️推奨: オブジェクトをトップレベルにする
795801

796802
```js
797803
{
@@ -804,7 +810,7 @@ DBなどから取得したレコードをそのまま配列のままで返すの
804810
}
805811
```
806812

807-
`非推奨: 配列をトップレベルにする`
813+
❌️非推奨: 配列をトップレベルにする
808814

809815
```js
810816
[
@@ -822,6 +828,30 @@ DBなどから取得したレコードをそのまま配列のままで返すの
822828
| | クライアント側で items項目を取得すると言ったひと手間が必要だが、大した労力ではない |
823829
| 性能 | データサイズが多少大きくなるが、レスポンスは通常gzip圧縮されることもあり誤差 |
824830

831+
## 値が存在しないという状態の表現
832+
833+
原則 `null` を用いず、パラメータのキー自体を含めないこと(`undefined`)による表現を行う。理由は以下の通り。
834+
835+
- ペイロードサイズを小さくできるため
836+
- 必要なプロパティのみが含まれている方が視認性が良いため
837+
- 実装上の不具合が無く、OpenAPI定義としても `null` 許容する方が手間がかかるため
838+
839+
✅️推奨: scoreがの値が存在しない場合、キー自体を含めない
840+
841+
```json
842+
{ "id": "00001", "name": "Bob" }
843+
```
844+
845+
❌️非推奨: scoreの値が存在しない場合はnullを用いる
846+
847+
```json
848+
{ "id": "00001", "name": "Bob", "score": null }
849+
```
850+
851+
参考:
852+
853+
- [OpenAPIにおけるundefinedとnullの設計 | フューチャー技術ブログ](https://future-architect.github.io/articles/20211028b/)
854+
825855
## 日付フォーマット
826856

827857
日付項目はUnix タイムスタンプを返すか、ISO8601で返すかの考慮がある。
@@ -1320,10 +1350,10 @@ sequenceDiagram
13201350

13211351
それぞれのメリット/デメリットは以下のとおり。
13221352

1323-
| | 同期API | 非同期API |
1324-
| :--------- | :-------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------- |
1325-
| メリット | ・各言語でデファクトスタンダードとなる実装があり、比較的低コストで実装可能 | ・重量級の処理の場合、呼び出し側がブロックされず快適なUX提供につながる ・アプリケーションの機能とリソースが新しいリクエストの処理のために解放される |
1326-
| デメリット | ・完了するまで呼び出し側処理がブロックされる ・応答時間が各サービスの応答時間の合算になるなど、ユーザーの待機時間が長くなる可能性がある | ・非同期タスクが失敗した場合の考慮など、設計/実装コストが高い |
1353+
| | 同期API | 非同期API |
1354+
| :--------- | :------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------ |
1355+
| メリット | ・各言語でデファクトスタンダードとなる実装があり、比較的低コストで実装可能 | ・重量級の処理の場合、呼び出し側がブロックされず快適なUX提供につながる<br> ・アプリケーションの機能とリソースが新しいリクエストの処理のために解放される |
1356+
| デメリット | ・完了するまで呼び出し側処理がブロックされる<br> ・応答時間が各サービスの応答時間の合算になるなど、ユーザーの待機時間が長くなる可能性がある | ・非同期タスクが失敗した場合の考慮など、設計/実装コストが高い |
13271357

13281358
同期APIと非同期APIの選択における本規約の推奨は以下。
13291359

0 commit comments

Comments
 (0)