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に設定する。





