API

Swagger API(REST API)とDB定義チェック表

 

API・DB設計でレビュー頂いた数々を大公開…。😭😭😭

 

Swagger Editor API設計

 

 

現在要件にないものはAPI設計に含めない

YAGNIの法則。

将来の拡張を見越して作っておいちゃえ!っと含めるとアプリ開発側で謎のエンドポイントや属性になる😥

現在の要件のみ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に設定する。

 

 

 

Amazonおすすめ

iPad 9世代 2021年最新作

iPad 9世代出たから買い替え。安いぞ!🐱 初めてならiPad。Kindleを外で見るならiPad mini。ほとんどの人には通常のiPadをおすすめします><

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)