API・DB設計でレビュー頂いた数々を大公開…。😭😭😭
もくじ
Swagger Editor API設計
現在要件にないものはAPI設計に含めない
将来の拡張を見越して作っておいちゃえ!っと含めるとアプリ開発側で謎のエンドポイントや属性になる😥
→
現在の要件のみAPI設計に含める
キャメルケース、パスカルケースの混在
- キャメルケース
/refreshToken - パスカルケース(ケバブケース, お団子ケース)
/refresh-token
どちらかに統一すること。混在はだめ!
GETパラメータなしのエンドポイントの400
- GETのアクセスかつパラメータもないのに、400(BadRequest)はありえない。
- Swaggerの設計に加えてはいけない
ユーザが自由入力しないのに400は設定しない
- 既読
- お気に入り登録、削除
- 退会
の送信など。
→
アプリ側での送信によるものは400(BadRequest)は設定しない。
- プロフィールの編集
- パスワードの登録変更
→ユーザの自由入力が伴うものに対して400を設定する
既読送信
/users/me/read-notices: post: summary: お知らせ既読の送信 description: | お知らせ既読であることを送信する。 tags: - read requestBody: $ref: '#/components/requestBodies/ReadNoticeIds' responses: '204': description: 成功時のレスポンス '400': ←●いらない $ref: '#/components/responses/400BadRequestError' ←●いらない '401': $ref: '#/components/responses/401UnAuthorizedError' '403': $ref: '#/components/responses/403ForbiddenError' '500': $ref: '#/components/responses/500InternalServerError' /users/me/read-all-notices: post: summary: お知らせ全権既読の送信 description: | お知らせ既読であることを送信する。 tags: - read responses: '204': description: 成功時のレスポンス '400': ←●いらない $ref: '#/components/responses/400BadRequestError' ←●いらない '401': $ref: '#/components/responses/401UnAuthorizedError' '403': $ref: '#/components/responses/403ForbiddenError' '500': $ref: '#/components/responses/500InternalServerError'
お気に入り送信
/users/me/add-favorite: post: summary: お気に入り追加の送信 description: | Postのidを指定してお気に入りであることを登録する tags: - favorite requestBody: $ref: '#/components/requestBodies/FavoriteBody' responses: '204': description: 成功時のレスポンス '400': ←●いらない $ref: '#/components/responses/400BadRequestError' ←●いらない '401': $ref: '#/components/responses/401UnAuthorizedError' '403': $ref: '#/components/responses/403ForbiddenError' '500': $ref: '#/components/responses/500InternalServerError'
配列の中に配列!?
❌
Hoge: type: integer description: ほげさん情報 example: $ref: '#/components/schemas/Hoge'
このまま書くと配列の中に配列が発生。。
😃
Hoge: $ref: '#/components/schemas/Hoge'
こうする。
レファレンス先で定義しておくこと。
セキュリティの認証のあり、なし
/getAnimals: get: security: [] // ←●これを記述すると認証なしでアクセスできる、を意味する。 ・・・
security: []は認証なしでアクセスできるものを表す。
- /loginならこのsecurity: []を記述して認証を無効化する必要がある
- /userme/profileならsecurity: []は記述しない
→会員情報をログインせずにアクセスできたらおかしい。
WEBがある場合
- WEBで公開されている情報を取得するエンドポイントは認証をつけてはいけない
一件だけ取得した場合にも配列で返すのか?
サーバスタータス 200と204の使い分け
すべて200にしていないか?
- アクセスしてresponseBodyを返却するサーバスタース
→ 200 - 更新処理などで成功かつresponseBodyは返さないサーバステータス
→ 204
@see
エラーコード
- 400: BadRequest
リクエストが不正または異常です。 - 401: UnAuthorized
未認証、または権限がありません。 - 403: Forbidden
紐づけられていない会員IDです - 404: NotFound
エンドポイントがありません - 500: ServerError
サーバサイドエラー
ログインが必要のないエンドポイントで401エラーのレスポンスがある
paths: /login: post: security: [] summary: ログインの実行 description: | メールアドレス/パスワードを用いてログインする。 tags: - auth requestBody: $ref: '#/components/requestBodies/Login' responses: '200': description: 成功時のレスポンス content: application/json: schema: type: object properties: token: $ref: '#/components/schemas/Token' '401': ←●おかしい $ref: '#/components/responses/ErrorUnAuthorized' ←●おかしい '403': $ref: '#/components/responses/ErrorForbidden' '500': $ref: '#/components/responses/ErrorInternalServerError'
必須な要素であればrequiredをつける
https://swagger.io/docs/specification/data-models/data-types/
レスポンスで利用しない属性を返してしまっている
最小限の情報を返すように設計しましょう。
requestBodyで送ってはいけない属性をオブジェクトに設定してしまっている
- パスワード
→
・ログインの時のみに送る。
・汎用のプロフィールオブジェクトで送るのはおかしい。 - 通貨, コイン, ポイント
→
・ユーザ側から送るのはおかしいので属性を持たない。
・サーバ側で処理してレスポンスのみ受け取るようにする - 画像URL
→
Multipart Requestsで画像そのものを送信するのが普通
WEBアプリの感覚をひきづらない
Patch, Put, Deleteの場合はPOST
例) 検索フォーム
REST APIではGET+クエリパラメータ
enumの不使用
定型的に決まっている値を返すならenum型を利用する
- 使う文字列決まってるならenumが使える
- タイプや追加予定のないカテゴリーなど
enumを利用したActivityスキーマの例
Activity: description: ポイント履歴情報 type: object properties: id: $ref: '#/components/schemas/BigIntId' user_id: type: integer format: int64 description: 会員情報のid example: 1 change_coin: type: integer description: ポイントの増減 example: 20 remaining_coin: type: integer description: ポイント残高 example: 2048 activity_type: type: string enum: - checkin - pay description: | 履歴タイプ - checkin: チェックイン。ポイントは増減 - pay: ポイント利用。ポイントは減少
履歴のオブジェクトで日時を返していない
- 表示で日付がなくても忘れずに。
- アプリ側で利用する
JSONのboolean型はtrue/false
push_receive_status: type: boolean description: | プッシュ通知を受け取るかどうかの真偽値 example: true
TINYINT(0, 1)ではない。
datetimeのフォーマットは書式も書く
Notice: type: object description: プッシュ通知で送信するお知らせオブジェクト properties: id: $ref: '#/components/schemas/BigIntId' name: type: string description: お知らせタイトル example: 機種変更時のアカウント移動について main_text: type: string description: お知らせ本文 example: メールアドレスとパスワードで引き継ぎが可能です。 is_read_flag: type: boolean description: 既読かどうかを表す真偽値 example: true created_at: type: string description: | お知らせ作成日時 * 書式 - Y-m-d H:i:s format: datetime example: '2019-01-02 01:00:21'
これ
created_at: type: string description: | お知らせ作成日時 * 書式 - Y-m-d H:i:s format: datetime example: '2019-01-02 01:00:21'
お気に入りやチェックインみたいなスイッチタイプのものはエンドポイントは1つ
/users/me/push-favorite: post: summary: お気に入りの登録/解除 description: | Postのidを指定してお気に入りであることを登録/解除する tags: - favorite requestBody: $ref: '#/components/requestBodies/FavoriteBody' responses: '204': description: 成功時のレスポンス '401': $ref: '#/components/responses/401UnAuthorized' '403': $ref: '#/components/responses/403Forbidden' '500': $ref: '#/components/responses/500InternalServerError'
FavoriteBody: required: true content: application/json: schema: type: object properties: post_id: type: integer description: 投稿記事id example: 1 required: - post_id example: post_id: 10
一覧ページにはoffsetやlimitが必要
/notices: get: summary: お知らせ情報一覧 description: | プッシュ通知お知らせ情報画面に表示される要素を取得 parameters * Limit - 取得上限。デフォルト25 * Offset - 取得開始位置。デフォルト0 tags: - notice parameters: - $ref: '#/components/parameters/Limit' - $ref: '#/components/parameters/Offset' responses: '200': description: 成功時のレスポンス content: application/json: schema: type: object properties: notices: $ref: '#/components/schemas/Notices' '401': $ref: '#/components/responses/401UnAuthorized' '403': $ref: '#/components/responses/403Forbidden' '500': $ref: '#/components/responses/500InternalServerError'
components: securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: JWT parameters: Limit: name: limit description: 取得件数を指定。デフォルト25。 in: query required: false schema: type: integer format: int64 Offset: name: offset description: 複数取得データのオフセットを指定。デフォルト0。 in: query required: false schema: type: integer format: int64
詳細ページにはoffsetやlimitはいらない
/notices/{notice_id}: get: summary: お知らせ情報詳細 description: | プッシュ通知お知らせ情報idで指定して詳細情報を取得 tags: - notice parameters: - name: notice_id in: path description: 取得したいお知らせのID required: true schema: type: string responses: '200': description: 成功時のレスポンス content: application/json: schema: type: object properties: notice: $ref: '#/components/schemas/Notice' '401': $ref: '#/components/responses/401UnAuthorized' '404': $ref: '#/components/responses/404NotFound' '500': $ref: '#/components/responses/500InternalServerError'
multipart/form-dataだとstringしか使えない & nullを使えない
例
Profile: required: true content: multipart/form-data: ←●ファイルアップロードでbinaryを扱いたい為のmultipart/form-data schema: type: object properties: name: description: 会員名 type: string example: 名無 花子 email: type: string description: メールアドレス example: gon86@example.net img_file: description: プロフィール画像バイナリデータ type: string format: binary ←●binary利用 example: 'ff d8 ff e0 00 10 4a 46 49 46 00 01 01 01 00 48 ...' push_receive_status: type: string ←●booleanではない! description: プッシュ通知を受け取るかどうかを表す真偽値 example: true
ここポイント
push_receive_status: type: string ←●booleanではない! description: プッシュ通知を受け取るかどうかを表す真偽値 example: true
@see
https://swagger.io/docs/specification/describing-request-body/multipart-requests/
DB定義 マイグレーションファイル
deleted_atでデリートフラグ
- Laravelであれば『delete_flag』はいらない。
- deleted_atだけで良い。
複合プライマリキー
複合プライマリキーのテーブルなら、きちんとマイグレーションファイルで定義する。
フラグ関連のテーブル
Boolean型かつデフォルトを0に設定する。