LoginSignup
23
17

More than 5 years have passed since last update.

Haskell Servant 入門 (Hello world)

Last updated at Posted at 2016-05-28

Hello world in Haskell with Servant

連載一覧

Haskell Servant フレームワークの便利な使い方などを紹介する連載記事です。
今後は以下に記事を追加していきます。
(2016年7月22日 6つ目の記事を追加しました!)

  1. Hello World
  2. Logger
  3. HTTPS
  4. Testing
  5. Database
  6. Strongly Typing

概要

本記事では Haskell Servant の導入方法をご紹介します。

記事内のすべてのコードは github リポジトリに置いてあります。
http://github.com/algas/haskell-servant-cookbook

想定する読者

  • Haskell の入門書を読んだことがある
  • 言語を問わず Web アプリケーションを触ったことがある
  • RDB (Relational Database) の基本的な知識がある

Haskell や Web フレームワーク自体について本記事では詳しく説明しません。

Haskell Servant とは?

Haskell の Web フレームワークで、以下の特徴があります。

  • 機能は少なめでシンプル(フルスタックではない)
  • 型による強い制限を持つ
  • Client と Server を同じ API から生成できる

プロジェクト作成

haskell-stack がインストールできているものとします。
Terminal で下記のコマンドを実行すると、myproject ディレクトリに servant プロジェクトの雛形が作成されます。

$ stack new myproject servant

API

"/" で Hello World を返し、
"/users/(name)/(age)" で User を返すシンプルな API を定義します。
User 型は name, age という属性を持っています。

lib/HelloApi.hs
{-# LANGUAGE DataKinds         #-}
{-# LANGUAGE DeriveGeneric     #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeOperators     #-}
module HelloApi where

import           Data.Aeson
import           Data.Proxy
import           Data.Text                  (Text)
import qualified Data.Text                  as T
import           GHC.Generics
import           Servant.API

data User = User
    { name :: Text
    , age  :: Int
    } deriving (Eq, Show, Read, Generic)
instance FromJSON User
instance ToJSON User

type HelloAPI  = Get '[PlainText] Text
            :<|> "user" :> Capture "name" Text :> Capture "age" Int :> Get '[JSON] User

helloApi :: Proxy HelloAPI
helloApi = Proxy

Server

HelloApi を呼び出し、サーバとして機能させます。

server/Main.hs
{-# LANGUAGE OverloadedStrings #-}
module Main where

import           HelloApi
import           Network.Wai
import           Network.Wai.Handler.Warp
import           Servant
import           Servant.API

server :: Server HelloAPI
server = hello :<|> user
    where
        hello = return "Hello world"
        user n a = return $ User n a

app :: Application
app = serve helloApi server

main :: IO ()
main = do
    run 8080 app

Client

HelloApi を呼び出し、クライアントとして機能させます。

client/Main.hs
{-# LANGUAGE OverloadedStrings #-}
module Main where

import           Control.Monad.Trans.Except (ExceptT, runExceptT)
import           Data.Text                  (Text)
import qualified Data.Text                  as T
import           HelloApi
import           Network.HTTP.Client        (Manager, defaultManagerSettings,
                                             newManager)
import           Servant.API
import           Servant.Client

hello :<|> user = client helloApi

queries :: Manager -> BaseUrl -> ExceptT ServantError IO (Text, User)
queries manager baseurl = do
    h <- hello manager baseurl
    us <- user "John Smith" 26 manager baseurl
    return (h, us)

main :: IO ()
main = do
    manager <- newManager defaultManagerSettings
    let baseUrl = BaseUrl Http "localhost" 8080 ""
    res <- runExceptT $ queries manager baseUrl
    case res of
        Left err -> putStrLn $ "Error: " ++ show err
        Right p -> do
            print p

動作確認

  1. ビルドする
    $ stack build
  2. サーバを起動する
    $ stack exec hello-server
  3. curl でサーバにアクセスしてみる
    $ curl http://localhost:8080
    Hello world
    $ curl http://localhost:8080/users/hoge/3
    {"age":3,"name":"hoge"}
  4. クライアントでサーバにアクセスする
    $ stack exec hello-client
    ("Hello world",User {name = "John Smith", age = 26})

クライアントからサーバにアクセスして値を取得することができました。
サーバとクライアントで同じAPIを使っているので、冗長な記述を減らせるのが Servant のメリットの1つです。

動作環境

  • stack:lts-6.0
  • servant-0.7

参考文献

連載予定

Haskell Servant 入門シリーズを連載で執筆予定です。
お楽しみに。

23
17
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
17