purescript-bucketchainの紹介

はじめに

rackやexpress, koaのようなウェブサーバーのための共通のインターフェースを提供するライブラリをつくりました。 それがpurescript-bucketchainです。

背景

外部のnpm packageに頼らず、ランタイムがnodejsなのでパイプで上手くやってくれるようにストリームベースなインターフェースで、koaとかexpressっぽさを踏襲しつつ、ミドルウェアを積みまくってサーバーを構築できるような基盤がほしくなりました。

使い方の流れ

  1. ミドルウェアを用意
  2. ミドルウェアを使ってサーバー作成
  3. サーバー起動

ミドルウェアを用意する

特定の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

MiddlewareHandler (Maybe ResponseBody) -> Handler (Maybe ResponseBody)の型シノニムです。 つまりnextHandler (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

さて、MiddlewareHandler (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
  }

詳しくはサンプルコードを見るとよりわかりやすいかもしれません。

既存のミドルウェア

Bucketchain · GitHub

こちらに現在8個のミドルウェアができています。 Bucketchainはあくまでインターフェースで、これに則ってミドルウェアとしてライブラリやフレームワークをつくるという世界観なので、より具体例を見るのであれば、こちらをご参照ください。