Haskell Servant docs provide several examples for writing an API to serve some content with a type level DSL like this
type API = "position" :> Capture "x" Int :> Capture "y" Int :> Get '[JSON] Position
What I want to do is write a similar API that can take several inputs as a GET request that also works from a browser. (Following code is abridged and simplified).
type QuestionAPI = "question"
:> QueryParam "question1" Question
:> QueryParam "question2" Question
:> QueryParam "question3" Question
...
...
:> QueryParam "questionn" Question
:> Get '[JSON] [Answer]
This works just fine but the function that consumes this endpoint takes in n
number of arguments
processQuestionAPI :: Maybe Question -> Maybe Question -> ... -> Handler [Answer]
processQuestionAPI param1 param2 param3 ... paramN = ...
which makes everything more difficult to read and reason with.
The first fix I could think of was to use a record!
data LotsOfQuestions = LotsOfQuestions
{ question1 :: Maybe Question
, question2 :: Maybe Question
, question3 :: Maybe Question
...
...
, questionn :: Maybe Question
}
and rewrite the endpoint like this
type QuestionAPI = "question"
:> ReqBody '[FromUrlEncoded] LotsOfQuestions
:> Get '[JSON] [Answer]
While compiling this code GHC threw this error
• No instance for (Web.Internal.FormUrlEncoded.FromForm
LotsOfQuestion)
arising from a use of ‘serve’
So I did just that wrote a custom FromForm
instance for LotsOfQuestions
.
instance FromForm LotsOfQuestion where
fromForm aForm = LotsOfQuestions
<$> parseMaybe "q1" aForm
<*> parseMaybe "q2" aForm
...
<*> parseMaybe "qN" aForm
Everything compiled and the server was up and running but I couldn't connect to it using my browser.
The URL I used was this
localhost:8081/questions?q1=what&q2=where&q3=when
The odd thing was that cURL
actually worked!
This
curl -d "q1=what&q2=where&q3=when" -X GET "localhost:8081/questions"
produces exactly what I want.
Looking around I found this this issue, which led me to believe that sending Request Body with a GET
request isn't the recommended way of doing things.
So I have to replace ReqBody
with something equivalent for GET
request, but I'm not sure what that could be.