purescript-bucketchainの紹介
はじめに
rackやexpress, koaのようなウェブサーバーのための共通のインターフェースを提供するライブラリをつくりました。 それがpurescript-bucketchainです。
背景
外部のnpm packageに頼らず、ランタイムがnodejsなのでパイプで上手くやってくれるようにストリームベースなインターフェースで、koaとかexpressっぽさを踏襲しつつ、ミドルウェアを積みまくってサーバーを構築できるような基盤がほしくなりました。
使い方の流れ
ミドルウェアを用意する
特定のpathで特定の画像を返すミドルウェアをつくってみましょう。 まずは以下を見てください。
middleware :: Middleware middleware next = do http <- ask if requestMethod http == "GET" && requestURL http == "/img" then liftEffect do setStatusCode http 200 setHeader http "Content-Type" "image/png" Just <<< fromReadable <$> createReadStream "example/300x300.png" else next
Middleware
はHandler (Maybe ResponseBody) -> Handler (Maybe ResponseBody)
の型シノニムです。
つまりnext
はHandler (Maybe ResponseBody)
です。
middleware1 <<< middleware2
のような形でミドルウェアを結合して組み合わせて一つのミドルウェアをつくります。
ResponseBody
はその名の通りレスポンスのボディですが、実態はReadable Streamとなっており、Readable StreamであればResponseBodyに変換可能です。また、String
からResponseBody
をつくることも可能です。
Maybe ResponseBody
となっていますが、Nothing
の場合だとボディ無しでレスポンスを返します。
当然next
を呼び出さなければ次のミドルウェアの処理には移りません。
Handler
モナドは、非同期な処理が中心のnodejsで、ミドルウェアがシーケンシャルに呼び出されることを保証するため、Aff
をベースにしています。また、nodejsはrequest streamとresponse streamを1リクエストで扱いますが、それらを1つにまとめたHttp
型をもっています。
早い話、具体的な型定義はnewtype Handler a = Handler (ReaderT Http Aff a)
となっています。
サーバーを作成
まずはこちらを見てください。
server :: Effect Server server = createServer middleware
さて、Middleware
はHandler (Maybe ResponseBody) -> Handler (Maybe ResponseBody)
でした。最後のnext
には何が渡されるのでしょうか?
createServer
の処理の中で、最後のnext
には、404を返すだけのHandler
が渡されます。
つまり、どのミドルウェアの処理にも引っかからないと404を返すようになっています。
サーバーを起動
最後はさっきのサーバーを起動するだけでサーバー出来上がりです! かなり簡単ですね。
main :: Effect Unit main = server >>= listen opts opts :: ListenOptions opts = { hostname: "127.0.0.1" , port: 3000 , backlog: Nothing }
詳しくはサンプルコードを見るとよりわかりやすいかもしれません。
既存のミドルウェア
こちらに現在8個のミドルウェアができています。 Bucketchainはあくまでインターフェースで、これに則ってミドルウェアとしてライブラリやフレームワークをつくるという世界観なので、より具体例を見るのであれば、こちらをご参照ください。