Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Offer HandlerM built on Aff (Asynchronous Eff) #15

Closed
dvdsgl opened this issue Mar 4, 2015 · 5 comments
Closed

Offer HandlerM built on Aff (Asynchronous Eff) #15

dvdsgl opened this issue Mar 4, 2015 · 5 comments
Milestone

Comments

@dvdsgl
Copy link

dvdsgl commented Mar 4, 2015

It would be really cool if HandlerM were a transformer on Aff (https://github.com/slamdata/purescript-aff), or to offer AsyncHandlerM so we could more easily write Handlers with lots of callbacks:

asyncHandler :: AsyncHandler
asyncHandler = do
  info <- liftAff $ JQuery.get "/info.json"
  users <- liftAff $ Database.query "SELECT * FROM USERS"
  sendJson { info: info, users: users }

app = do
  get "/foo" asyncHandler

Doing something like this with capture can get ugly.

@nkly
Copy link
Contributor

nkly commented Mar 5, 2015

Haven't heard of Aff, but it's definitely better than capture. Thanks!

@michaelficarra
Copy link
Contributor

👍, I was just trying to use purescript-express with redis, and the node redis library is entirely asynchronous (and my wrapper for it is built on Aff), so I have no way to write a handler that can respond based on my interaction with redis. 😢

edit: I ended up doing something like this for now:

liftCallback :: forall a eff. ((a -> Eff eff Unit) -> Eff eff Unit) -> (a -> HandlerM Unit) -> HandlerM Unit
liftCallback f next = do
  cb <- capture next
  liftEff (f cb)

foreign import insert :: forall eff. String -> String -> Database -> (Boolean -> DBEff eff Unit) -> DBEff eff Unit

... do
  ...
  liftCallback (insert key value db) $ \success ->
    ...

but Aff support would be so much better.

@nkly nkly added this to the 0.3.0 milestone Jul 14, 2015
anttih added a commit to anttih/purescript-express that referenced this issue Jul 30, 2015
* Changes HandlerM to use Aff (asynchronous effects) instead of Eff
* Adds a type parameter (`e`) for the effect rows to HandlerM to get rid of
  `unsafeInterleaveEff`. Normally when writing top-level handlers one can
  write:

      myHandler :: forall e. Handler e

  which is a type alias for

      myHandler :: forall e. HandlerM (express :: EXPRESS | e) Unit

* Removes `capture` as it's no longer needed. Async code can be wrapped with
  Aff and `Aff` can be lifted to `HandlerM` with `liftAff`.
anttih added a commit to anttih/purescript-express that referenced this issue Jul 30, 2015
* Changes HandlerM to use Aff (asynchronous effects) instead of Eff
* Adds a type parameter (`e`) for the effect rows to HandlerM to get rid of
  `unsafeInterleaveEff`. Normally when writing top-level handlers one can
  write:

      myHandler :: forall e. Handler e

  which is a type alias for

      myHandler :: forall e. HandlerM (express :: EXPRESS | e) Unit

* Removes `capture` as it's no longer needed. Async code can be wrapped with
  Aff and `Aff` can be lifted to `HandlerM` with `liftAff`.
stkb pushed a commit to stkb/purescript-express that referenced this issue Oct 31, 2015
* Changes HandlerM to use Aff (asynchronous effects) instead of Eff
* Adds a type parameter (`e`) for the effect rows to HandlerM to get rid of
  `unsafeInterleaveEff`. Normally when writing top-level handlers one can
  write:

      myHandler :: forall e. Handler e

  which is a type alias for

      myHandler :: forall e. HandlerM (express :: EXPRESS | e) Unit

* Removes `capture` as it's no longer needed. Async code can be wrapped with
  Aff and `Aff` can be lifted to `HandlerM` with `liftAff`.
@nkly nkly closed this as completed in 3d4524b Nov 6, 2015
nkly pushed a commit that referenced this issue Nov 6, 2015
@kika
Copy link
Contributor

kika commented Jul 29, 2016

How should I handle errors in Aff? I have a code like this:

upstreamAuth (AppStateRecord state) token = do
  let opts = state.upstream 
           <> method := "POST"
           <> path := "/_session"
  reqdata <- liftEff $ Buffer.fromString token UTF8
  res     <- SR.simpleRequest opts reqdata
  pure res.body

checkAuth::forall e. AppState -> Token -> Aff (ref::REF, http::HTTP|e) Cookie
checkAuth stateref token = do
  state <- liftEff $ readRef stateref
  case getSession token state of
    Nothing      -> upstreamAuth state token
    Just session -> pure session.cookie

authHandler :: forall e.  AppState -> Handler (console::CONSOLE, http::HTTP, ref::REF | e)
authHandler statedata = do
  token <- getRequestHeader "Authorization"
  case token of
    Nothing   -> nextThrow $ error "NOAUTH"
    Just user -> case parseUser user of
      "" -> nextThrow $ error "NOAUTH"
      u  -> do
        liftEff $ log ("API token: " <> u)
        cookie <- liftAff $ checkAuth statedata u
        liftEff $ log ("Cookie: " <> cookie)
        next

And the errors which happen in upstreamAuth are propagated to runtime exceptions without any possibility to handle them. I suppose that's where I should use runAff, and put next and nextThrow as success and failure callbacks but it requires handlers which return Eff, not HandlerM, as runAff does. Obviously, I'm missing something.
How do you handle errors with Aff "subrequests" from Handlers?

@kika kika mentioned this issue Jul 29, 2016
@Risto-Stevcev
Copy link

@kika you can use attempt from the Aff lib

@kika
Copy link
Contributor

kika commented Jul 29, 2016

@Risto-Stevcev right, that's what I've started with, but then it introduces another case ... of Left -> ... Right -> ... and I'm trying to join the Destroy All Ifs sect :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants