inuinu blog(開発用)

BOT @wagagun の開発ノウハウや、IT向け?の雑記ブログです。

なんちゃってRESTful APIについて(概要編)

この記事では、RESTful APIが実現できない言語系やフレームワーク向けに、より近い実装方法を解説します。
RESTful APIの落とし穴も解説します。

こちらは主にSQL等、DBの照会・更新の手法は知っているが、APIからWebアプリ開発を始めてみようとしている方を対象にしているため、初心者向けの情報もありますが、ご了承ください。

APIとは

APIをざっくり説明すると、OSやアプリ等のシステムの機能を、他のアプリケーションでも利用できるようにした仕組みです。
これにより開発言語が異なっても、APIを使用するプラグイン等が用意されてれば、比較的容易にそれを利用できるようになります。

Web APIとは

Web APIについては、前項の「ご使用のOS等のシステムの機能」を「そのサイトのサービスで表示している情報」に置き換えたものだと思ってください。

さらに、アクセスはWeb=HTML同様、HTTPプロトコルにて行います。

違いは、取得できるコンテンツが、デザイン装飾の付いたHTMLではなく、XMLJSONという、データとして直に利用可能なフォーマットで受け取れることです。

Web APIの具体例(サービスとして公開されたもの)

一般的にはこのようなサービスです。

  • 天気・気温や、株価や為替レートといった情報を取得するサービス
  • Amazon楽天のように、商品の値段やタイトルなどを取得できるサービス
  • SelesforceやSlack、Twitterといった、外部サービスやSNSは手作業で入力するが、これを自動化するサービス
  • Googleドライブやboxは手動でアップロードするが、これを自動化するサービス

公開するといっても、悪意のある第三者からのデータの改ざんを防ぐため、ユーザー登録に加え、アプリケーション登録を行い、トークンを取得して、そのトークンにてアクセスすることが主流となっています。

Web APIの具体例(クローズドなもの)

公開されてはいないものは、例えば、社内のイントラネットサービスです。
申請・承認といったワークフローシステムや、費用精算や、勤怠管理、自前のナレッジサイト、他社とのBtoBなどです。

Ruby on RailsJava系のフレームワークなどのサーバーサイドスクリプトで開発すれば、DBはほぼ直接SQLで、あるいは言語特有の関数・メソッド群(ActiveRecordなど)を利用してアクセスすると思います。
この場合は、(公開されたAPIを利用する以外)APIのお世話になる行ことは、まずないでしょう。

ただ、以下のような開発環境(開発状況)では、ターゲットとなるDBにアクセスができません。

  • 100%ピュアなクライアントサイドスクリプトでの開発(例えばVue.js)
  • パッケージのサーバーサイドスクリプトによっては、特定のDBサーバーのアクセスが不可能なケース

他にもあるかもしれませんが、私の経験したものは、この2つのケースです。

このように、クローズドなシステムでのWeb APIの活用は、クライアントの言語でのデータ取得の不備・不可能を補完するために活用されます。

100%ピュアなクライアントサイドスクリプトでの開発

100%ピュアなクライアントサイドスクリプトでの開発、例えばJavascriptのみでのシステム開発では、SQLを直接発行できません。

その場合は、以下のように解決します。

  • クライアントサイドスクリプトではSQLを発行できない
  • 代わりにDBアクセス用のWebサーバーを別途立ち上げ、そちらにAPIプログラムを作成する
  • 欲しい情報✕更新・照会の数だけプログラムを用意する
  • APIプログラムは、サーバーサイドからのパラメータをもとに、照会・更新を行い、その結果をJSONで返す
  • クライアントサイドスクリプトAJAXの技術を用いてAPIにアクセスし、結果、受け取ったJSONをもとに適切な処理を続行する

特定のDBシステムのアクセスが不可能なケース

「特定のDBシステムのアクセスが不可能なケース」とは、どのようなケースがあるでしょうか?
以下は私が経験したケースです。

  • BtoB系(あるいはEC)パッケージを導入
  • そのパッケージは、在庫管理や流通用の基幹システムに接続する必要があるが、その言語ではアクセス不可能
  • 基幹システムとSQLでやり取りできる他の言語にて、別途APIサーバーを作成し、そこにアクセスし、データの照会・更新を行うこととした

私が何度か経験したものとしては、DBシステムとSQLでやり取りする際にはJDBCを利用するしかないといった場合、Web言語側がJDBCをサポートしていないために、そのDBとやり取りするために、JDBCが利用できる別の言語でAPIサーバーを立ち上げるといったケースです。
ならば、最初からJDBCが利用できるWeb言語のほうが良いかというと、そうでもないことのほうが多いです。

業務のジャンルによっては、非Java系のWeb言語のほうが、無償も含めテンプレートとなるパッケージが豊富に存在するようです。 また、デザイン性や生産性の問題で非Java系のWeb言語を敬遠する向きもあるようです。
欠点があるために、使いにくい言語に統一するのでは、生産性も悪く、見た目もダサいかもしれません。それぞれの長所を活かすようにしましょう。

Web APIの理想の一つにRESTful APIがあるが

Web APIの理想の一つにRESTful APIがあります。
が、理想を追い求めると、こういったAPIを作ることになるでしょう。

どこまでが狭義のRESTful APIなのかは知りませんが、今まで経験した中での最強クラスをリストアップしています。

  • 処理の違いはMETHODで表現(GET/POST/PUT/PATCH/DELETE)
  • URLに動詞を含めない
  • URLに拡張子は含めない
  • ユーニークなキーとなるidは、URLに含める
    • https://~/user/123でMETHODがDELETEの場合は、ユーザー123を削除する
    • https://~/company/456/sales/2023でGETの場合は、会社コード456の2023年の売上情報を取得する
    • その他のオプションパラメータはGETパラメータ(?aaa=bbb&ccc=ddd)でいい
  • POSTの場合のパラメータはフォームデータではなく、BODYにJSONを埋め込む
  • データが存在しない場合は404、データ追加は201、更新や削除は204…と使い分ける

ですが、以下の点で不可能あるいはおすすめできません。

  • PUT/PATCH/DELETEが未サポートあるいはデフォルトではない
  • 上記の制限により「URLに動詞を含めない」ルールが実現できない
  • 「URLに拡張子は含めない」が実現できない言語・フレームワークもある
  • ユーニークなキーとなるidをURLに含めることができない言語・フレームワークもある
  • 「POSTの場合もパラメータはJSONで」では添付ファイルが送れない
  • 「データが存在しない場合は404、データ追加は201、更新や削除は204…と使い分ける」だと、つくり手が混乱する

以降に、問題点の詳細と解決策を提示します。

「PUT/PATCH/DELETE」が無理ならば

IISでは、確かデフォルトではNGだったと思います。

また、PUT/PATCH/DELETEはブラウザでは未サポートです。
(裏技的なやり方はあるようですが…)

ですので、開発時のわずらわしさから、GET/POSTのみで対応するようにしています。

「URLに動詞を含めない」が無理ならば

上記のようにGET/POSTのみにすると、私の経験では、このような状況になります。

  • GETの場合は照会・削除、あるいはトグルのON/OFF
  • POSTは登録あるいは変更

この場合は、動詞はURL上に含めたほうが良いかなと。

  • get_xxxx
  • delete_xxxx(あるいはcancel_xxxx)
  • toggle_xxxx
  • set_xxxx

上記のようなプログラム名でAPIプログラムを作成することが多いです。

「URLに拡張子は含めない」が無理ならば

素のphpやColdfusionでは拡張子が出てくるので、それは仕方ないかなと。

これは、API云々ではなく、URLアドレスを別途定義できるか、フォルダ構成をそのままURLにするかといった、Web言語(あるいはフレームワーク)の仕組みの違いかと思います。

「ユーニークなキーとなるidは、URLに含める」が無理ならば

こちらも、フォルダ階層がそのままURLになるようなタイプのWeb言語では、(可変値になるようなフォルダ構成は)実現できません。
パラメータはすべて、通常のGETパラメータで統一します。
(POSTの場合は、hidden値にします。)

「POSTの場合もパラメータはJSONで」が無理ならば

実際の開発では、添付ファイルを受け取るケースが多々あります。
この添付ファイルは、電子帳簿保存法施行により、レシートを添付し、DBの日付カラムでタイムスタンプの機能を代用したいといったニーズが、更に増えてくると思います。

このケースではフォームデータ(multipart/form-data)にするしか無いのですが、こちらのPOSTはフォームデータ、あちらはJSON…ではメンテナンス性に欠けてしまいますので、私はフォームデータに統一しています。

行(レコード)が可変である等の理由で、どうしてもJSONで渡したい場合は、フォームデータとした上で、hiddenにJSONを挿入して渡す手段もあります。

「データが存在しない場合は404、成功時も201・204と使い分ける」が無理ならば

404はURLが見つかりませんでも使われます。Webアプリケーションが不調な場合もWebサーバーが404を返すような場合、システム的に混乱を招くため、これは却下しました。

(サービス名は伏せますが)404をURL Not Foundと、レコードのNot Foundの両方で使用していた、某データベースサービスがありますが、この違いはJSON上にあるエラーメッセージで判断するようになっていました。
しかし、一度削除したレコードにアクセスすると、レコードのNot Foundではなく、URL Not Foundのメッセージが返ってきます。 私は不具合に感じましたが、誰も見て見ぬふりをしているような気がします。
あまり凝らないほうが良いですね。

20xのバリエーション(201・204)も混乱するので200に統一。

こちらも、404で取り上げたサービスですが、更新や削除では204(204 No Content)は「正常終了しました。特に何も無いので、JSONはおろか、BODYには何も送りませんよ。」というステータスを返してきます。
これですと、JSONを期待してロジックを組むと、異常終了しますよね。
また204は削除したというステータスと勘違いしやすいです。ただ、更新しましたというステータスはありません。
204を使用しているサービスに文句を言うのは筋違いかもしれませんが、使いにくさは感じます。

結果的に、私がAPIを作成する場合は、このようにさせてもらっています。

  • 追加・更新・削除に関わらず、正常終了は常に200で返す、URLミスは404、APIシステム自体のバグによるエラーは500で返す
  • システム的に可能であれば、404や500でもJSONで渡せるようにする
  • Data Not Foundは、404ではなく「”record_count” : 0」で判断してもらう
    (あるいはデータの有無のフラグ値を渡す。)
  • 入力値エラー、更新エラーも200とした上で、JSONで表現し、具体的なエラー内容(メッセージテキスト)も返す

結果的になんちゃってRESTful APIとなる

まあ、それで良いと思います。

私は、ほとんど「無理」なWeb言語・フレームワークAPIを作成することのほうが多い気がします。

できる限り、素早く割り切って、お互いに作りやすいシステムを作成することが大切ですね。

処理手順について

処理手順については、下記のコラムを参照してください。

inuinu-tech.hatenablog.jp

さらに、実践的な記事も用意しています。

inuinu-tech.hatenablog.jp

それでは、また。