【Servant】(3) エンドポイントを増やす

[【Servant】(1) Wai - Qiita](https://qiita.com/sand/items/16a6ef431a8376e7cbaa)
[【Servant】(2) Servantチュートリアル - Qiita](https://qiita.com/sand/items/fea7c294dd7835692ff7)
[【Servant】(3) エンドポイントを増やす - Qiita](https://qiita.com/sand/items/3af359b1830453d1d0ed)
[【Servant】(4) URLパラメータをハンドラの引数とする - Qiita](https://qiita.com/sand/items/d1c0bfb84487a1dfa9bb)
[【Servant】(5) JSON - Qiita](https://qiita.com/sand/items/fd2ea8acdc56998d5c5e)
[【Servant】(6) HTML - Qiita](https://qiita.com/sand/items/0e9f3c80b5103b966be4)
[【Servant】(7) Post Data - Qiita](https://qiita.com/sand/items/d21aed7c24521e20f1cb)
[【Servant】(8) Another Monad - Qiita](https://qiita.com/sand/items/9c8fa50815b00daead24)
[【Servant】(9) Handlerモナド - Qiita](https://qiita.com/sand/items/ae649f14f1b598d636c3)
[【Servant】(10) SQLite - Qiita](https://qiita.com/sand/items/091b0ea981152ad7d744)
[【Servant】(11) Servant-Client - Qiita](https://qiita.com/sand/items/a0e7758da7274a119849)
[【Servant】(12) Basic 認証 - Qiita](https://qiita.com/sand/items/dba678cef365fbd23ae3)







今回も公式ドキュメントに沿って話を進めます。前回作成したexampleのエンドポイントを増やしてみます。
[Docs » Tutorial » Serving an API - A first example](https://haskell-servant.readthedocs.io/en/stable/tutorial/Server.html)


# 1. エンドポイントを増やす

前回のソースです。

```haskell:Lib.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE DeriveGeneric #-}

module Lib
    ( runServant
    ) where

-- import Servant(serve, Proxy(..), Server, JSON, Get, (:>))
import Servant
import Data.Aeson(ToJSON)
import Data.Time.Calendar
import GHC.Generics(Generic)
import Network.Wai(Application)
import Network.Wai.Handler.Warp(run)

data User = User
  { name :: String
  , age :: Int
  , email :: String
  , registration_date :: Day
  } deriving (Eq, Show, Generic)

instance ToJSON User


users1 :: [User]
users1 =
  [ User "Isaac Newton"    372 "isaac@newton.co.uk" (fromGregorian 1683  3 1)
  , User "Albert Einstein" 136 "ae@mc2.org"         (fromGregorian 1905 12 1)
  ]

type UserAPI1 = "users" :> Get '[JSON] [User]

server1 :: Server UserAPI1
server1 = return users1

userAPI :: Proxy UserAPI1
userAPI = Proxy

-- 'serve' comes from servant and hands you a WAI Application,
-- which you can think of as an "abstract" web application,
-- not yet a webserver.
app1 :: Application
app1 = serve userAPI server1

runServant :: IO ()
runServant = run 8081 app1
```



以下の部分を書き換えます。

```haskell:Old
users1 :: [User]
users1 =
  [ User "Isaac Newton"    372 "isaac@newton.co.uk" (fromGregorian 1683  3 1)
  , User "Albert Einstein" 136 "ae@mc2.org"         (fromGregorian 1905 12 1)
  ]

type UserAPI1 = "users" :> Get '[JSON] [User]

server1 :: Server UserAPI1
server1 = return users1
```


APIのエンドポイントを3つに増やします。対応するハンドラも3つにします。3つのエンドポイントとハンドラは**順番で対応付けされます。**
見ての通り、複数のエンドポイントを連結するときも、複数のハンドラを連結するときも **:<|>** が使われています。型構成子と値構成子の違いを認識するのは重要です。

```haskell:New
isaac :: User
isaac = User "Isaac Newton" 372 "isaac@newton.co.uk" (fromGregorian 1683 3 1)

albert :: User
albert = User "Albert Einstein" 136 "ae@mc2.org" (fromGregorian 1905 12 1)

users2 :: [User]
users2 = [isaac, albert]

type UserAPI2 = "users" :> Get '[JSON] [User]
           :<|> "albert" :> Get '[JSON] User
           :<|> "isaac" :> Get '[JSON] User

server2 :: Server UserAPI2
server2 = return users2
     :<|> return albert
     :<|> return isaac
```


置き換えた後の全ソースを掲載します。



```haskell:Lib.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE DeriveGeneric #-}

module Lib
    ( runServant
    ) where

import Servant
import Data.Aeson(ToJSON)
import Data.Time.Calendar
import GHC.Generics(Generic)
import Network.Wai(Application)
import Network.Wai.Handler.Warp(run)

data User = User
  { name :: String
  , age :: Int
  , email :: String
  , registration_date :: Day
  } deriving (Eq, Show, Generic)

instance ToJSON User


isaac :: User
isaac = User "Isaac Newton" 372 "isaac@newton.co.uk" (fromGregorian 1683 3 1)

albert :: User
albert = User "Albert Einstein" 136 "ae@mc2.org" (fromGregorian 1905 12 1)

users2 :: [User]
users2 = [isaac, albert]

type UserAPI2 = "users" :> Get '[JSON] [User]
           :<|> "albert" :> Get '[JSON] User
           :<|> "isaac" :> Get '[JSON] User

server2 :: Server UserAPI2
server2 = return users2
     :<|> return albert
     :<|> return isaac

userAPI :: Proxy UserAPI2
userAPI = Proxy

-- 'serve' comes from servant and hands you a WAI Application,
-- which you can think of as an "abstract" web application,
-- not yet a webserver.
app2 :: Application
app2 = serve userAPI server2

runServant :: IO ()
runServant = run 8081 app2
```

動作を確認します。

パス"users"にアクセスすると2 Userが返ってきます。

```
$ curl http://localhost:8081/users
[{"email":"isaac@newton.co.uk","registration_date":"1683-03-01","age":372,"name":"Isaac Newton"},
{"email":"ae@mc2.org","registration_date":"1905-12-01","age":136,"name":"Albert Einstein"}]
```

パス"albert"にアクセスするとalbertが返ってきます。

```
$ curl http://localhost:8081/albert
{"email":"ae@mc2.org","registration_date":"1905-12-01","age":136,"name":"Albert Einstein"}
```

パス"isaac"にアクセスするとisaacが返ってきます。


```
$ curl http://localhost:8081/isaac
{"email":"isaac@newton.co.uk","registration_date":"1683-03-01","age":372,"name":"Isaac Newton"}
```

いい感じです。



#2. serverの順番を変更する

それではもう少しServantの挙動を確認します。Serverの定義を少し変更して挙動を見てみます。

server2の1行目と3行目を入れ替えます。[User]とUserでは型が違うのでコンパイルエラーとなることを期待します。

```
type UserAPI2 = "users" :> Get '[JSON] [User]
           :<|> "albert" :> Get '[JSON] User
           :<|> "isaac" :> Get '[JSON] User

server2 :: Server UserAPI2
server2 = return isaac
     :<|> return albert
     :<|> return users2
```

実際コンパイルすると、コンパイルエラーで怒られます。エラーメッセージはあまりわかり易くはないですが。


次にserver2のisaacとalbertを入れ替えてみます。どちらもUser型なのでコンパイルが通ることが期待されます。

```
type UserAPI2 = "users" :> Get '[JSON] [User]
           :<|> "albert" :> Get '[JSON] User
           :<|> "isaac" :> Get '[JSON] User

server2 :: Server UserAPI2
server2 = return users2
     :<|> return isaac
     :<|> return albert
```

実際にコンパイルすると、成功します。ただしcurlでisaacパスを叩くとalbertが返ってきて、albertパスを叩くとisaacが返ってきます。これも期待通りです。


なんとなくServantの感じがつかめてきました。このシリーズは続きます。