Giter Club home page Giter Club logo

reflex-servant's Introduction

reflex-servant lets you access Servant APIs with Reflex FRP.

  • API calls are as simple as Event in -> m (Event out).
  • Minimal dependencies, covered by just reflex and servant-client-core.
  • Customizable product types
  • traverseEndpoint for labeled events or sequential calls

Quick Start

Let's start with some pragma's and imports.

{-# LANGUAGE ConstraintKinds, DataKinds, FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings, RankNTypes, ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications, TypeOperators #-}
import Control.Monad
import Control.Monad.IO.Class
import Data.Proxy
import Data.Text(Text)
import Reflex
import Reflex.Servant
import Servant.API
import Servant.Client.Core

... and an example API:

type Api = "ping" :> Get '[JSON] Text
      :<|> "calendar" :> Capture "calendarName" Text :> "items" :> Get '[JSON] [(Timestamp, Text)]
      :<|> "calendar" :> Capture "calendarName" Text :> "items" :> ReqBody '[JSON] (Timestamp, Text) :> Post '[JSON] [(Timestamp, Text)]

-- not important
type Timestamp = Int

Now let's build a simple network. We'll pretend to use reflex-dom.

myApp :: forall t m. MonadWidget t m => m ()
myApp = do
  postBuild <- getPostBuild
  let ping :<|> list :<|> post = reflexClient (basicConfig myRunner) (Proxy @Api)
  calendarResponse <- list ("Birthday calendar" <$ postBuild)
  calendar <- holdDyn [] (filterRight calendarResponse)
  showCalendar calendar
  
showCalendar :: MonadWidget t m => Dynamic t [(Timestamp, Text)] -> m ()
showCalendar = void . dyn . fmap (undefined :: [(Timestamp, Text)] -> m ())

The runner is provided by a library, such as servant-client or servant-client-jsaddle.

Here's what the runner types look like.

myRunner :: ServantClientRunner () m
myRunner cfg (GenericClientM m) = myRunServantClient cfg m

myRunServantClient :: () -> (RunClient m' => m' a) -> m (Either ServantError a)
myRunServantClient cfg m = error "Call servant-client or servant-client-jsaddle"

The redundant cfg :: () is useful when calling multiple services. You can ignore it for now.

Advanced use

While the above approach is easy, it is not quite as flexible. If you want to use your client functions in a transformed monad, or if you're not comfortable passing the client functions as arguments, you can use a different configuration.

pingCalendar :<|> listCalendar :<|> postCalendar =
  reflexClient (calendarConfig) (Proxy @Api)

calendarConfig = (defaultConfig myRunner)
  { configEndpoint = ConfiguredEndpointConfig ()
  }

myApp' :: forall t m. MonadWidget t m => m ()
myApp' = do
  postBuild <- getPostBuild
  calendarResponse <- endpoint' listCalendar ((,) "Birthday calendar" <$> postBuild)
  calendar <- holdDyn [] (filterRight calendarResponse)
  showCalendar calendar

endpoint'
    :: PerformEvent t m
    => Endpoint () i o
    -> Event t i
    -> m (Event t (Either ServantError o))
endpoint' = endpoint myRunner

If your application needs to access multiple APIs, you can make use of a configuration argument to distinguish between APIs.

data Service = CalendarService | OtherService

pingCalendar' :<|> listCalendar' :<|> postCalendar' =
  reflexClient (calendarConfig') (Proxy @Api)

calendarConfig' = (defaultConfig myRunner)
  { configEndpoint = ConfiguredEndpointConfig CalendarService
  }

endpoint''
    :: PerformEvent t m
    => Endpoint Service i o
    -> Event t i
    -> m (Event t (Either ServantError o))
endpoint'' = endpoint multiRunner

multiRunner
    :: Service
    -> GenericClientM a
    -> m (Either ServantError a)
multiRunner CalendarService = myRunner ()
multiRunner OtherService = error "Other service runner not configured yet."

Of course variations are possible. Instead of a closed Service type, you may want to pass something more flexible, such as the runner itself, or you can use an open union if you want to substitute different runners for testing.

Literate Haskell

This is a literate readme, so you can actually typecheck and run this document!

main = pure ()

Host-independent

To illustrate the use of reflex-servant we have pretended to use reflex-dom, but actually this library does not depend on any particular reflex host. These are the fake definitions use to typecheck this document.

type MonadWidget t m = (Reflex t, MonadHold t m, MonadSample t (Performable m), PerformEvent t m, MonadIO (Performable m), PostBuild t m)

dyn :: MonadWidget t m => Dynamic t (m a) -> m (Event t a)
dyn = undefined

simpleTextInput :: MonadWidget t m => m (Dynamic t Text)
simpleTextInput = undefined

datePicker :: MonadWidget t m => m (Dynamic t Timestamp)
datePicker = undefined

reflex-servant's People

Contributors

roberth avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.