見出し画像

OpenAPI による REST APIの設計

こんにちは。NOBORIの水澤です。

弊社が展開しているPHRアプリのバックエンドにいくつかのサーバーがあり、APIを通じてクライアント/サーバー間のデータのやり取りを行っています。
今回はそのAPIの設計について触れようと思います。


REST API

REST APIについてはQiitaに公開されている記事がわかりやすくて良いです。翻訳: WebAPI 設計のベストプラクティス

APIはインターフェースなので、仕様を公開して使ってもらうことが大事です。
APIを利用する側は、APIのドキュメントから仕様を確認して使用するに値するものなのか、判断すると思います。
そのドキュメントは様々な形で公開されていて、資料が散らばっていてどこを見たらいいのかわからない、そもそも資料が更新されていなかったりなど、不便この上ないです。

そんなときにOpen API形式での設計が威力を発揮します。

Open API

Open API 正しくは Open API Specification (OAS) と呼ばれています。
OASになるまでには色々と段階を経ていますが、ここでは割愛します。
・OpenAPI Specification
https://swagger.io/specification/

OASは、REST APIの仕様の定義書を作成するフォーマットのことです。
OAS形式の定義書はyaml、json形式で記述されます。

Open API形式のIF定義書とは

今回はサンプルを用意してみました。
下記がOAS3.0で記述されたIF定義書になります。

openapi: 3.0.0
servers:
 - url: 'http://localhost:8080/'
info:
 description: OpenAPIによるWebAPI設計
 version: 0.1.0
 title: Sample API
tags:
 - name: Country
   description: 国に関する操作API
 - name: Capital
   description: 首都に対する操作API
paths:
 /country/list:
   post:
     tags:
       - Country
     summary: 世界各国の国情報を返却する。※Sampleとしてアメリカ、インド、オーストラリア、日本とする。
     description: 国情報として国名、人口、主な産業を返却する
     operationId: getCountryInfoList
     responses:
       '200':
         description: successful operation
         content:
           application/json:
             schema:
               $ref: '#/components/schemas/ResCountryInfoList'
       '401':
         description: Unauthorixed
 /capital/list:
   post:
     tags:
       - Capital
     summary: 世界各国の首都情報を返却する。※Sampleとしてアメリカ、インド、オーストラリア、日本とする。
     description: 首都情報として首都名、国名、協定世界時との時差を返却する
     operationId: getCapitalInfoList
     responses:
       '200':
         description: successful operation
         content:
           application/json:
             schema:
               $ref: '#/components/schemas/ResCapitalInfoList'
       '401':
         description: Unauthorixed
components:
 parameters: {}
 requestBodies: {}
 schemas:
   ResCapitalInfoList:
     type: object
     description: 首都情報結果
     properties:
       capitalInfoList:
         type: array
         items:
           $ref: '#/components/schemas/CapitalInfo'
         example:
           - capitalName: Washington
             countryName: United States of America
             timeDifference: '-4:00'
           - capitalName: New Delhi
             countryName: India
             timeDifference: '+5:30'
           - capitalName: Canberra
             countryName: Australia
             timeDifference: '+10:00'
           - capitalName: Tokyo
             countryName: Japan
             timeDifference: '+9:00'
     xml:
       name: ResCapitalInfoList
   CapitalInfo:
     type: object
     description: 首都情報
     properties:
       capitalName:
         type: string
         description: 首都名
         example: Tokyo
       countryName:
         type: string
         description: 国名
         example: Japan
       timeDifference:
         type: string
         description: 協定世界時との時差
         example: '+9:00'
     xml:
       name: CapitalInfo
   ResCountryInfoList:
     type: object
     description: 国情報結果
     properties:
       capitalInfoList:
         type: array
         items:
           $ref: '#/components/schemas/CountryInfo'
         example:
           - countryName: United States of America
             population: 約 33009 万人
             majorIndustries: 工業全般
           - countryName: India
             population: 約 137860 万人
             majorIndustries: 農業、鉱業
           - countryName: Australia
             population: 約 2573 万人
             majorIndustries: 農業、鉱業
           - countryName: Japan
             population: 約 12630 万人
             majorIndustries: 製造業
     xml:
       name: ResCountryInfoList
   CountryInfo:
     type: object
     description: 国情報
     properties:
       countryName:
         type: string
         description: 国名
         example: Japan
       population:
         type: string
         description: 人口数
         example: '約 126,300,000 人'
       majorIndustries:
         type: string
         description: 主要産業
         example: 製造業
     xml:
       name: CountryInfo

このIF定義書には下記が記載されています。

・openapi
Open API 形式の宣言

・servers
接続先のサーバーURL

・info
IF定義書の情報(定義書のタイトルなど)

・tags
各APIを区分けするタグ情報

・paths
APIのパス、および仕様

・components
APIで使用されるEntityや型情報

これだけではIF定義書として読みにくいですね。
そこでSwagger Editorに登場してもらいます。

※サンプルで用意したIF定義書はGithubに公開しています。
 以降は下記を使用していただいても良いです。
https://github.com/psp-engineer/SampleAPI

Swagger Editor

まずはこちらにアクセスください。
https://editor.swagger.io/

Swagger Editorが起動したと思うので、そこに先程のIF定義書を読み込ませてください。
※左ペインにペーストするか、Githubに公開しているSampleAPI.yamlを読み込ませてみてください。

画像1

こんな感じに右ペインにAPIの内容が出てきたと思います。
サンプルとして、国情報を取得するAPI (/country/list)、首都情報を取得するAPI (/capital/list) が表示されていると思います。

画像2

こちらは国情報を取得するAPI (/country/list) の詳細を表示しています。
上からAPIの説明/詳細、API呼び出しに必要なRequestParameter、返却値となるResponseParameterが記載されています。

ResponseParameterには返却値の例も記載されています。
これもIF定義書の中に記述されていて、サーバー起動したときにスタブデータとして実際に返却可能です。

では、実際にサーバー機能を起動してみましょう。

サーバー機能の出力と接続テスト

Swagger EditorはIF定義書の内容を基に、サーバー/クライアントのIFモジュールを自動生成してくれます。
Swagger EditorのGenerate Serverからモジュールを出力してみてください。
出力可能な選択肢がいっぱいあって困っちゃいますね!
今回はお手軽にサーバー起動したいので、nodejs-serverを出力します。

nodejs-server-server-generated.zipというファイルがDLできるので、それをお好きな場所に展開してください。
展開するとサーバー機能の基となるIFモジュールが入っています。
今回はNode.jsで出力したので、READEME.mdの通りに起動してみます。

画像3

Node.jsでサーバー機能をローカルで起動してみました。
では、次にSwagger Editorから国情報を取得するAPI (/country/list)を呼び出すコマンドを出力してみましょう。

画像4

Swagger EditorのParametersに「Try it out」のボタンがあると思います。
そこからExecuteボタンをクリックすると、Curlのコマンドが発行されます。
これをTerminalから実行してAPIにアクセスしてみましょう。

画像5

IF定義通りのResponseが返ってきましたね。

クライアント機能の出力

先程はサーバー機能の出力を行い、スタブデータの返却をしてみました。
同様にSwagger Editorからはクライアント機能も出力可能です。
この機能が非常に強力で、サーバー/クライアント機能が同一のIF定義書から出力されるので、IF部分に間違いが起こりません。
外部接続の実装は非常に面倒でテストも難しいですが、OASで記述されたIF定義書があればIF部分での不要なバグを減らすことが期待できます。
今回は割愛しますが、皆さんもSampleAPIを使ってクライアントモジュールからの接続を試してみてください。

おわりに

駆け足でOpen APIについてご紹介しました。
Open APIを使うことで、以下のメリットがあります。

・APIドキュメント/IF定義書の自動生成
・サーバー/クライアント機能の自動生成
・Swagger Editorの強力な開発機能によるコーディング/テスト

弊社のような自社サービスの開発はもちろん、広く世間に公開するAPIを開発する場合においても、サーバー/クライアントの開発を異なるエンジニアがする場面では役に立つフォーマット/ツールだと思います。
皆さんもぜひ使ってみてください。