How to process form in hypertrout EDIT: How to use ReqBody in hypertrout


I found working example on how to work with forms in hyper and examples in hypertrout for routing, but not sure how to combine them, I have separated function that defines resources and function that create router, but return type of resources is disalloving me from calling parseForm.

module Main where

import Prelude
import Control.Monad.Indexed.Qualified as Ix
import Control.Monad.Except (ExceptT)
import Control.Monad.Reader (ReaderT, ask, runReaderT)
import Data.Maybe (fromMaybe, Maybe)
import Effect (Effect)
import Effect.Aff (Aff)
import Hyper.Conn (Conn)
import Hyper.Status (Status)
import Hyper.Middleware (Middleware)
import Hyper.Node.Server (defaultOptionsWithLogging, runServer', HttpRequest, HttpResponse)
import Hyper.Response (closeHeaders, respond, writeStatus, StatusLineOpen, ResponseEnded)
import Hyper.Trout.Router (RoutingError, router)
import Text.Smolder.HTML.Attributes (method, for, id, name)
import Text.Smolder.HTML (p, form, label, input, button)
import Text.Smolder.Markup (text, (!))
import Type.Proxy (Proxy(..))
import Type.Trout (type (:=), type(:<|>), Resource)
import Type.Trout.ContentType.HTML (class EncodeHTML, HTML)
import Type.Trout.Method (Get, Post)

type ComponentsType = Record ()

type ConnHttp resp = Conn HttpRequest (HttpResponse resp) ComponentsType

type Env = String

type AppM m = ReaderT Env m

data GreetForm = GreetForm

data GreetPost = GreetPost String

type Site = "greeting" := Resource (Get GreetForm HTML :<|> Post GreetPost HTML)

instance encodeHTMLGreetForm :: EncodeHTML GreetForm where
  encodeHTML GreetForm = 
    form ! method "post" $ do
      label ! for "name" $ text "Name"
      input ! id "name" ! name "name"
      button $ text "Greet"

instance encodeHTMLGreetPost :: EncodeHTML GreetPost where
  encodeHTML (GreetPost g) = p (text g)

runAppM ∷ forall a. String -> (AppM Aff a) -> Aff a
runAppM = flip runReaderT

site :: Proxy Site
site = Proxy

resources :: forall m. Monad m => 
  { "greeting" ::
    { "GET" :: ExceptT RoutingError (AppM m) GreetForm
    , "POST" :: ExceptT RoutingError (AppM m) GreetPost
resources = 
  { "greeting":
    { "GET": pure GreetForm
    , "POST": (GreetPost <$> ask) -- here call somehow parseForm??

onRoutingError :: Status -> Maybe String -> Middleware (AppM Aff) (ConnHttp StatusLineOpen) (ConnHttp ResponseEnded) Unit
onRoutingError status msg =
  writeStatus status
  respond (fromMaybe "" msg)

handler :: Middleware (AppM Aff) (ConnHttp StatusLineOpen) (ConnHttp ResponseEnded) Unit
handler = router site resources onRoutingError -- I would not call parseForm here because it would be called on every request??

main :: Effect Unit
main = runServer' defaultOptionsWithLogging {} (runAppM "Ahoj") handler


Found out that actualy I’m supposed to use hyperroute for this, but could anybody provide any example of how to use it? Mainly what to pass to ReqBody second argument ctx


It is probably not supported, so maybe I will migrate to nodetrout as recommended

I’m triing to look at it from other perspective, I wrote function to handle post with all actions but types at resources are crashing as expected…

greetPostAction :: Middleware (AppM Aff) (ConnHttp StatusLineOpen) (ConnHttp StatusLineOpen) GreetPost
greetPostAction = do
  eitherForm <- parseForm
  pure $ case eitherForm of
    Right params ->
      case (required "name" params) of
        Right name -> GreetPost name
        Left error -> GreetPost error
    Left error -> GreetPost error

resources :: forall m. Monad m => 
  { "greeting" ::
    { "GET" :: ExceptT RoutingError (AppM m) GreetForm
    , "POST" :: ExceptT RoutingError (AppM m) GreetPost
resources = 
  { "greeting":
    { "GET": pure GreetForm
    , "POST": greetPostAction

Ok, now I found out that actualy I’m not supposed to handle this using Hyper but using Trout