見出し画像

仮想政府ニャルニアの歩み:2日目・OpenAPIとJsonSchemaってだいたい一緒じゃない?

諸行無常:API仕様はいつまでたっても完成しないことを言い表す言葉。

執務室にて

ここはニャルニア国の執務室である。一匹の猫が公共サービスを共通のAPIとして実装するべく動き出していた。ニャルニアは猫の国である。ニャルニアに公共サービスはない。公共サービスを新規実装するならITを使えと飼い主に教えられてきた。

ニャンマルハウトニャルニア国のマイナンバーを定義した。

つぎは、新しくマイナンバーを手に入れるためのAPIを設計しようとしている。親友ししゃも丸と共に、今日もニャルニア国公共サービスAPIを作る。

設計は大事だ。ところで設計と仕様って使い分けが難しい言葉だよね。API設計はAPI仕様の策定で、API実装とは分離している。一方で、API実装の設計も別途必要である。

マイナンバー管理APIの仕様策定

ニャンマルハウト「では実際にマイナンバー管理APIの仕様を策定していく」

ししゃも丸「仕様ってなに?」

ニャンマルハウト「どんなAPIにするのか、っていうことを考えるんだ。実際に作ることは実装という」

ししゃも丸「ていうか、そもそもAPIってなに?」

ニャンマルハウト「ここでいうAPIというのは、WebAPIだな。インターネット上にあって、だれでもプログラムを使ってアクセスできるものだ。例えば、ニャルニア国の国籍を取得するスマートフォンアプリを作る時に、このAPIにアクセスするプログラムを書くことになる。APIを作っておけば、みんなが好きなプログラムでニャルニア国の公共サービスにアクセスできるというわけだ」

ししゃも丸「なるほど!(わかってない)」

ニャンマルハウト「GraphQLもブームだが、クライアントの実装コストが増大するのが悩ましいところだ。バックエンドをRESTful APIにしておいてGraphQLを間に挟むというアプローチもあるので、今回はおおむねRESTful APIという方針にしよう」

ししゃも丸「はーい!」

ニャンマルハウト「RESTful APIはリソース指向APIということなので、とりあつかうリソースが何なのか、という定義からスタートする。ししゃも丸、マイナンバー管理APIでとりあつかうリソース……つまり、情報の塊がどんなものかわかるかい」

ししゃも丸「そうだな。情報の塊か。やっぱり、マイナンバーじゃないか。マイナンバー管理ってことは、一人ひとりのマイナンバーを扱うんだろう?」

ニャンマルハウト「そうだな。マイナンバーがリソースになると思うかもしれない。けど、マイナンバーっていうのはあくまで猫に結びついた番号なんだ。管理されるリソースは……猫だ!」

ししゃも丸「なん……だと……」

ニャンマルハウト「実際のところ、ニャルニア国籍を取得するということが、マイナンバー管理APIへの猫の登録という形式を取るわけだな。したがって、とりあえずマイナンバー管理APIと呼んでいたが、これは実際には国籍管理APIなんだ。その中でマイナンバーを使っている」

ししゃも丸「じゃあ、ニャルニア国籍を取得したらマイナンバーがもらえるってことか。俺もマイナンバーほしい。48bitの乱数を眺めてみたいんだ

ニャンマルハウト「もちろん実装まで終わったらししゃも丸に使ってもらいたい。けど1番は俺な

整理:今回の範囲はあくまでニャルニア国籍を登録したり、変更したりする。国籍を取得すると、自動的にマイナンバーが与えられる。国籍を取得せずにマイナンバーをもらうことは、今の所できない。

ニャンマルハウト「国籍というとややこしいが、要するにアカウントってことになる。ニャルニア国アカウントを持っていると、公共サービスが利用できる。このアカウントは実際の猫一匹に結びついている。アカウントは一度作成したら削除することはできない……死んだ時は、停止されるだけだ。死後の手続きっていうのもいろいろあるからな」

ししゃも丸「かなしい〜

ニャンマルハウト「生き物はいつか死ぬさ。実際のところ、ニャルニア国籍をもっていない猫でもニャルニア国の公共サービスを受けることができる、という場合もある。例えば、旅行でニャルニア国にきた猫も、ニャルニア国の公共交通機関を利用できるべきだろう。将来的には、こういった国籍と結びついていないマイナンバーも発行できるようにしていかないといけないかもしれないな」

日本の場合、こういうことにはならないはずです。そもそも公共交通機関の利用にマイナンバーは不要なので。ただ、ニャルニア国ではすべての公共サービスをAPIで実装しようとしているので、このようになるわけです。

ニャンマルハウト「とにかく最初はaccountだ。APIのエンドポイントはこんな感じになる」

POST accounts
GET accounts/{personal-identifier}
GET accounts/{personal-identifier}/records
POST accounts/{personal-identifier}/records/update
POST accounts/{personal-identifier}/records/close
POST accounts/{personal-identifier}/records/reopen

ニャンマルハウト「実際はこれだと色々問題があるが、一旦はこんな感じにしておこう。アカウント情報の更新や停止を POST にしているのは、アカウントリソースの性質を表現している」

エンドポイント:ざっくりいえばURL。ここにプログラムを使ってデータを投げたり、問い合わせたりすると、結果が返信される。

ニャンマルハウト「アカウントのデータ更新は自由に認められるべきではなく、また履歴=recordも保存されるべきだからだ。アカウントの情報変更履歴を作成する、停止履歴を作成する、という意味で、POSTを使っている」

ししゃも丸「難しいが、履歴書みたいなイメージ?

ニャンマルハウト「まあそうだな。次にアカウント情報の内容を決めていく。……こんなところか?」

{
    "name": "ししゃも丸",
    "personal-identifier": "xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx",
    "registered-at": "2020-06-01T12:00:00+0900",
    "birthday": "2018-02-14",
    "status": "active"
}

ししゃも丸「俺の情報だ! けど、マイナンバーがないね」

ニャンマルハウト「マイナンバーは実際にAPIが出来上がってから取得したいからな。とにかく、最低限の情報はこんな感じだろう」

ししゃも丸「年齢がないよ〜」

ニャンマルハウト「年齢は生年月日と現在時刻を比べて計算するから、データとして持っておくのは生年月日だけで大丈夫なんだよ」

ニャンマルハウト「つぎは履歴だな。account-recordは……こんな感じだろうか」

{
    "recorded-at": "2020-06-01T12:00:00+0900",
    "operation": "open"
}

ニャンマルハウト「これはもちろん最低限の情報で、実際にはいろいろと付け足す必要があるだろうな。それはおいおいやっていくことにしよう」

ニャンマルハウト「さてRESTful APIの仕様としてはおおざっぱにできてきたが、詳細な部分まで詰めたものを用意しておきたい。そこで、Open API をつかって API 仕様をきちんと定義しておくことにする」

openapi: 3.0.2
info:
 title: Nyarnia API
paths:
 "/accounts":
   post:
     summary: Register New Account
     responses:
       '200':
         content:
           application/json:
             schema:
               type: object
               properties:
                 account:
                   "$ref": "#/components/schemas/Account"
 "/accounts/{pid}":
   get:
     summary: Get Account
     parameters:
     - "$ref": "#/components/parameters/PersonalIdentifier"
     responses:
       '200':
         content:
           application/json:
             schema:
               type: object
               properties:
                 account:
                   "$ref": "#/components/schemas/Account"
 "/accounts/{pid}/records":
   get:
     summary: Get Account Records
     parameters:
     - "$ref": "#/components/parameters/PersonalIdentifier"
     responses:
       '200':
         content:
           application/json:
             schema:
               type: object
               properties:
                 account:
                   "$ref": "#/components/schemas/Account"
                 records:
                   "$ref": "#/components/schemas/AccountRecord"
               required:
               - records
 "/accounts/{pid}/records/{operation}":
   post:
     summary: Create Account Record
     parameters:
     - "$ref": "#/components/parameters/PersonalIdentifier"
     - in: path
       name: operation
       schema:
         type: string
         enum:
         - update
         - close
         - reopen
     responses:
       '201':
         content:
           application/json:
             schema:
               type: object
               properties:
                 record:
                   "$ref": "#/components/schemas/AccountRecord"
               required:
               - record
components:
 schemas:
   AccountRecord:
     type: object
     properties:
       recorded-at:
         type: string
         format: date-time
   Account:
     type: object
     properties:
       name:
         type: string
       birthday:
         type: string
         format: date
       personal-identifier:
         type: string
         format: uuid
       registered-at:
         type: string
         format: date-time
       status:
         type: string
         enum:
         - active
         - in-review
         - closed
         - stopped
     required:
     - name
     - birthday
     - personal-identifier
     - registered-at
     - status
 parameters:
   PersonalIdentifier:
     in: path
     name: pid
     schema:
       type: string
       format: uuid

ししゃも丸「長っ!」

ニャンマルハウト「たったの115行だよ」

ししゃも丸「ええ……」

HTTPについて

ニャンマルハウト「さて、ここまで当たり前のように説明をすっ飛ばしてきたが、そもそもWebAPIはどんな風にできているのか、という部分を解説していなかったな」

ししゃも丸「ええー、そういう解説いるの? あえて聞かなかったのに」

ニャンマルハウト「こらこら、俺を厄介なオタク猫みたいに扱うんじゃない

ししゃも丸(渋い顔)

ニャンマルハウト「実際、WebAPIという言葉に正確な定義は与えられていない。一般的には、Webの技術をAPI実装に転用したもののことだ。Webの技術というと、ブラウザ(SafariとかGoogleChromeとか)でウェブサイトを閲覧する技術のことだな」

ししゃも丸「ブラウザは俺もよくつかってるよ。セクシーな女の子の動画を見てる。人間も好きだよね、猫の動画

ニャンマルハウト「人間は猫が好きだからな。さて、このブラウザというのは大むね、URLを入力すると、そのURLの宛先から情報をダウンロードして、画面に表示するという機能を持ったソフトウェアだ」

ししゃも丸「ふむふむ。URLってさ、そもそもなんなの?」

ニャンマルハウト「あれは、データの住所だ。Webでアクセスできるデータにはほとんどすべて住所がある。その住所をURLと呼ぶんだ」

厳密にいうと住所ではないですが、たとえ話の例としてはそこそこ適切かと思います。

ニャンマルハウト「で、URLの冒頭に書いてある https:// だとか https:// だとかは、スキームと呼ぶ。ざっくりいえば通信のルールを書く場所だな。ftp://  とか git+ssh:// とか、実は http/https の他にも種類がある。ほとんどのURLが http/https からスタートするのは、ブラウザが原則的に http プロトコルという通信ルールを使っているからだ」

ししゃも丸「でもニャンマルハウト、俺のブラウザには http:// なんて表示されてないぜ」

画像1

ししゃも丸「こんな感じだ」

ニャンマルハウト「最近では、スキーマ部分を隠してしまうブラウザが多いからな。カーソルを当てたり、キーボードの矢印を打ったりすると、URLが全部表示されるぜ」

ししゃも丸「やってみる」

画像2

ししゃも丸「お、出た。たしかに https:// から始まってる」

ニャンマルハウト「この https というのは、セキュリティの高い http 通信という意味で、通信ルールそのものは http と同じだ。そして、HTTP というのは非常にシンプルな構成の通信ルールで、データの要求(HTTPリクエスト)と要求への返答(HTTPレスポンス)のペアだけしかない。問い合わせと応答の1ペアを1回の通信として取り扱うわけだ」

ししゃも丸「ボタンを押すと餌が出てくるってこと?

ニャンマルハウト「そういうこと」

ニャンマルハウト「さて、このHTTPはURLをサーバーに送信し、サーバーがURLに対応したデータを返送するという流れになっている。普通の場合、データとしてHTMLドキュメントが返ってくる。これをブラウザがうまく表示して、見た目のいいウェブサイトを楽しむことができる」

ししゃも丸「HTMLっていうのはわからないけど、ウェブサイトはHTMLっていう種類のデータってことなんだな。猫はどういう種類のデータ?

猫は液体じゃないっけ。

ニャンマルハウト「ただ、別にHTMLドキュメントを返すというルールはない。他のデータでもいい。画像や、ただのテキストや、圧縮ファイルでも大丈夫だ。そういうデータが返ってきた場合、ブラウザはデータをコンピューターにダウンロードしたり、あるいは表示できるデータなら表示したりする。HTMLという仕組みと、HTTPという仕組みは、それぞれ別のものなんだ」

ニャンマルハウト「なので、HTTPの返送データの形式を工夫すれば、HTTPを使ったプログラム向けのデータ通信を実現できる。こういう、ウェブサイトのための仕組みを活用してAPIを実装したものを、一般にはWebAPIと呼ぶわけだな」

ししゃも丸「専用に通信ルール作ったらだめなの?」

ニャンマルハウト「だめじゃないさ。ただ、そうすると考えることがたくさんあって大変だし、HTTP通信するためのプログラムっていうのはすでにたくさんある。ブラウザなんて使わなくても、たいていのプログラム言語はHTTP通信のためのライブラリが最初から用意されている。HTTP通信はルールが明確で、理解も比較的簡単だ。そういうわけで、HTTPを使ったほうが楽ちんなんだ」

ししゃも丸「なるほどなぁ。つまり、WebAPIっていうのはみんな使ってるよくあるやり方ってことか」

ニャンマルハウト「そういうことだ。実際、Twitterなんかは公開APIが用意されていて、これはHTTPの仕組みを利用したWebAPIとして実装されている」

まとめ

ししゃも丸「そういえばニャンマルハウト、OpenAPIの説明がないよ

ニャンマルハウト「そういえばそうだな……。まあ、WebAPIの仕様を書くためのルールだ。Google や Microsoft という人間のグループが作ったプロジェクトだ。便利なので、調べてみると良い」

ししゃも丸「ほへー。人間は集団で活動するのが得意だね

ししゃも丸「ところでタイトルにあったJsonSchemaってなに?」

ニャンマルハウト「…………」

ししゃも丸「JsonSchemaって……

ニャンマルハウト「…………」

今回の作業はこのコミットにまとめられています。

https://github.com/niaeashes/nyarnia/commit/b851045561b00bfbb2d520a989abfee6d8951553

次回予告

前途多難なニャルニア国の公共サービス実装、次回はデータベースの設計を行っていきます。

ニャンマルハウト「MySQLが突然MariaDBになった時、俺は宇宙猫になった」


この記事が気に入ったらサポートをしてみませんか?