Help us understand the problem. What is going on with this article?

Haskell rest フレームワークでREST API設計をサボる

More than 3 years have passed since last update.

追記

その後restの開発が微妙な感じなので、servantとかspock使うのをオススメします。
servantはtype level foobarをたくさん使っているので、弊社のブログ記事を参照してもらえるといいかもしれないです。

はじめに

Haskell界隈は素数クラスタの人とかが圏論を絡めたこわい話をするのが日常です。
まじこわい。
私にはそんなお話できませんが、日頃Haskellを使ってフリーランスの魔法少女(おっさん)をしている経験を生かして、何か実務ですぐに使えそうなお話をしようと思ってAdvent Calendarに登録しました。

今回は、Haskellの rest という(その名の通り)RESTフレームワークをご紹介します。
googlabilityのとても低い名前なので、正しく検索できているかはわかりませんが、
今日現在、このフレームワークについて触れている日本語の文献はここのプレスリリースみたいな記事だけです。

開発途上でまだ安定していないところもありますが、「おおっ」と思うところも多くあり注目しています。

なにがいいのか

近年SPAやスマフォのネイティブアプリに代表されるように、リッチなフロントエンドに対して、json(やxml)形式でデータを提供するサーバサイドを開発する需要が高まっています。
rest パッケージを使うと、Silk社が実際の運用の中で蓄積してきたベストプラクティスを反映させた様々な便利機能を使って容易にそういったシステムを組むことが可能になります。

特筆すべきは、データベースの定義をするだけで

  • サーバサイドのシステム
  • クライアントサイドで使うライブラリ
  • 仕様書

が全部作られてしまう点です。

その他かゆいところに手が届く機能についても以降で触れていきます。

なにができないのか

「Silk社が かんがえた さいきょう の REST API べすとぷらくてぃす」を簡単に実現するフレームワークのため、
その方針がうんこだと思う方や、その方針がうまく適合されないシステムを作成する場合には、残念ながら使えません。
そういう場合は手間が増えますが、scottyあたりを使うといいと思います。

とはいえ、下手な「オレオレREST API」なんかより、ずっとできのいいものができるはずなので、試してみても損はないのではないでしょうか。

つかいかた

チュートリアルがとても充実しているので、読むといいです。
この記事の内容もこのチュートリアルを勝手に焼き直したような内容です。
また、ブログサービスを題材にしたサンプルも用意されているので、これを手元に用意して試してみると理解が早いです。
今回はこのサンプルを使いながら、restの機能を紹介してみます。

コードの実装に触れる前に、まずどんなことができるのか遊んでみましょう。
ソースgit cloneしたディレクトリに入って、今回のサンプルテスト用にコンパイルします。

make init-sandbox
CABAL_FLAGS="--flags=wai" make

./.cabal-sandbox/bin/rest-example-genrest-example-waiが生成されます。

API サーバの起動

.cabal-sandbox/bin/rest-example-wai

でAPIサーバが立ち上がります。
デーモン化したい場合はangelを利用してください。

仕様書、クライアントサイドコードの出力

rest-example-genを使って仕様書やクライアントサイドで使うコードを出力できます。

./cabal-sandbox/bin/rest-example-gen --helpを実行すると「--helpなんてオプションねぇよクソ」というメッセージとともに
親切なUsageが表示されます。

rest-example-gen: user error (unrecognized option `--help'
Usage: rest-example-gen [OPTIONS...], with the following options:
  -d URLROOT   --documentation=URLROOT  Generate API documentation, available under the provided URL root.
  -j           --javascript             Generate Javascript bindings.
  -r           --ruby                   Generate Ruby bindings.
  -h           --haskell                Generate Haskell bindings.
  -s LOCATION  --source=LOCATION        The location of additional sources.
  -t LOCATION  --target=LOCATION        The target location for generation.
  -v VERSION   --version=VERSION        The version of the API under generation. Default latest.
  -p           --hide-private           Generate API for the public, hiding private resources. Not default.
)

なんかいろいろありますね。

仕様書の作成

試しに
.cabal-sandbox/bin/rest-example-gen -d './' -s rest-gen/files/Docs/
でドキュメントを生成してみましょう。
docsというディレクトリが作成され、その中にhtml形式のドキュメントが生成されるはずです。
-sオプションで指定しているのはドキュメントのテンプレートファイルで、今回はrest-gen内に用意されているデフォルトのテンプレートを使用しています。

生成されたドキュメントはこんな感じで、各APIについて一覧表示してくれます。
json形式もxml形式もいけるみたいですね。

ちなみに、上記コマンドで普通に生成したところ、.jsとか.cssのパスがおかしかったので、手作業で修正してあります。
たぶんrest-gen/files/Docs/のテンプレートがおかしいのでしょう。
issueにはなっているのでそのうち修正されると思います。

クライアントコードの作成

今度はクライアントサイドで使うjsのコードを出力してみましょう。

.cabal-sandbox/bin/rest-example-gen -j -t rest-example.js

rest-example.jsというファイルが作成されます。
使うときは

var api = new RestExampleApi(apiHost);

と宣言したあとで、

api.User.list().then(function (users) { console.log(users); });

のように使います。
User(登録ユーザ)のlist()(一覧)を全部もってきて、それに対して非同期の処理をthenで割り当てています。
実は、生成されたjsファイルはjQueryに依存していて、このapi.Post.list()JQueryPromiseであるため、thenが利用できます。

このlist()などのメソッドは、APIの実装をした.hsファイルの定義に対応しているのですが、先ほど生成したドキュメントには提示されていないので、正直使いにくいです。
開発がすすめば、クライアント用コードのドキュメントも出力できるようになるでしょう。

とはいえ、Angular.jsを使ってしまっている場合は$httpサービスを使わないと挙動がおかしくなってしまうなど、JQuery依存に起因する問題もあるため、実際にはドキュメントにしたがって自分の好きなようにAjax通信のコードを書いた方が得策なのが現状です。

REST APIの実装

ここまで、どんなことができるか見てみました。
では、実際にどうやってREST APIが実装できるか見てみましょう。
すなわち自分のプロジェクト用のrest-example-genなりrest-example-waiに相応するものが作リ方です。

今回はイケてる機能をかいつまんでとりあげます。
詳しくはチュートリアルやサンプルをご覧ください。

Routing

REST APIのルーティングを指定します。
以下のように直感的な記法で書くことができます。

blog :: Router IO IO
blog = root -/ user
            -/ post --/ comment

とてもかしこいです。
Template Haskellを使いすぎてよくわからないことになっているどこぞのフレームワークと比べてとてもかしこいですね!
説明はなくてもこれがどんなルーティングになるのか想像できると思います。

Resource

Routingの中のuserとかpostとかcommentといったものはResourceと呼ばれ、

  • どんなパラメタを要求するのか
  • どうやってレスポンスを構築するか

などを指定します。本体です。

面白いのは

  • あるデータの一覧を取得する
  • その中からIDを指定して特定のデータの詳細を取得する

というよくあるシチュエーションを簡単に実現する方法が用意されていることです。

resource.hs
module Api.Post (resource) where

import Rest
import qualified Rest.Resource as R

type Title = String

resource :: Resource IO (ReaderT Title IO) Title () Void
resource = mkResourceReader
  { R.name   = "post"
  , R.schema = withListing () $ named [("title", singleBy id)]
  , R.list   = const list
  , R.get    = Just get
  }

mkResourceReaderResourceを作成する関数の1つです。
R.nameはリソースの呼び名を設定します。
すなわち、下記のルーティング構成なら

routing.hs
routing :: Router IO IO
routing = root -/ resource1

/name-of-resource1resource1で設定したAPIを呼び出せるようになります。

さて、R.schemaが面白い仕組みを提供しています。
この記述で

  • /postにアクセスしたら一覧のリストを
  • /post/title/<title>にアクセスしたら<title>で見つかる唯一の要素を

返すAPIとして定義されます。
このように、筋のいいAPI設計に自然に従えるようになります。

具体的にどういったリストや要素を返すのかはR.listR.getで定義するのですが、
詳細はチュートリアルやサンプルをご覧ください。

Versioning

restパッケージは、構築したAPIに対してバージョン情報を必ず与える必要があります。

api.hs
api :: Api IO
api = [(mkVersion 1 0 0, Some1 blog)]

mkVersion関数の3つの数字が、

  • メジャーアップデート
  • マイナーアップデート
  • パッチ

に相応します。
クライアントからx.y.zのバージョンへのリクエストが来た時、サーバはx.y.w(wは最新の数字)へのリクエストとして自動的にあつかってくれます。
この機能によって、アップデートや保守を安全に行えるようになります。

まとめ

restフレームワークを使うことで、手軽に筋のいいREST APIを構築することができます。
まだまだ安定していないところもあり、これからといった状況ですが、Haskellを使ったアプリケーション開発を推し進める重要な役割を担ってくれることを期待します。

宣伝

ぼくと契約してHaskellでお仕事しようよ!!

ARoWでは

  • Haskellを使ったお仕事に興味がある人
  • 百合が好きな人

を募集しています。
twitterとかFacebookとかなんか適当にご連絡して、新時代の百合ゲームをぼくと一緒に作ってください。
Wantedlyは創業者とかが意識高くてまぶしいからこわくて使えないよ。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away