servant/servant-js
Steve Purcell 5188e842a9 [servant-foreign] Parameterise type classes with a foreign representation type
We allow a user-specified type to represent the foreign type of haskell
types encountered in the API. This lets users map Integer, Date etc. to
representations other than Text, and have those representations
available in the returned list of Req.

For example, we might want to map a type which has an instance of
Generic to both a foreign type name and a class declaration for that
foreign type such that it can encode/decode itself to JSON. The previous
limitation to a single Text output prevented this case.
2016-03-14 10:34:53 +13:00
..
examples stylish haskell changes 2015-08-18 00:07:12 +02:00
include less OverlappingInstances noise 2016-01-04 13:09:11 -05:00
src/Servant [servant-foreign] Parameterise type classes with a foreign representation type 2016-03-14 10:34:53 +13:00
test [servant-foreign] Parameterise type classes with a foreign representation type 2016-03-14 10:34:53 +13:00
CHANGELOG.md Changelogs for matrix params 2015-10-13 21:46:52 +02:00
docs.sh Replace servant-jquery with servant-js in remaining files 2015-11-02 19:59:46 +01:00
LICENSE Change copyright to servant contributors 2016-01-20 16:58:29 +01:00
README.md [servant-js] README-md - Fix broken link 2015-12-01 00:38:37 -05:00
servant-js.cabal Bump aeson version. 2016-02-18 22:45:05 +01:00
Setup.hs stylish haskell changes 2015-08-18 00:07:12 +02:00
tinc.yaml Use tinc on travis 2015-11-05 09:32:13 +08:00
TODO.md rename servant-jquery to servant-js, Servant.JQuery to Servant.JS 2015-07-22 19:25:02 +02:00

servant-js

servant

This library lets you derive automatically Javascript functions that let you query each endpoint of a servant webservice.

It contains a powerful system allowing you to generate functions for several frameworks (Angular, AXios, JQuery) as well as vanilla (framework-free) javascript code.

Example

Read more about the following example here.

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}

import Control.Concurrent.STM
import Control.Monad.IO.Class
import Data.Aeson
import Data.Proxy
import GHC.Generics
import Network.Wai.Handler.Warp (run)
import Servant
import Servant.JS
import System.FilePath

-- * A simple Counter data type
newtype Counter = Counter { value :: Int }
  deriving (Generic, Show, Num)

instance ToJSON Counter

-- * Shared counter operations

-- Creating a counter that starts from 0
newCounter :: IO (TVar Counter)
newCounter = newTVarIO 0

-- Increasing the counter by 1
counterPlusOne :: MonadIO m => TVar Counter -> m Counter
counterPlusOne counter = liftIO . atomically $ do
  oldValue <- readTVar counter
  let newValue = oldValue + 1
  writeTVar counter newValue
  return newValue

currentValue :: MonadIO m => TVar Counter -> m Counter
currentValue counter = liftIO $ readTVarIO counter

-- * Our API type
type TestApi = "counter" :> Post '[JSON] Counter -- endpoint for increasing the counter
          :<|> "counter" :> Get  '[JSON] Counter -- endpoint to get the current value

type TestApi' = TestApi -- The API we want a JS handler for
           :<|> Raw     -- used for serving static files

-- this proxy only targets the proper endpoints of our API,
-- not the static file serving bit
testApi :: Proxy TestApi
testApi = Proxy

-- this proxy targets everything
testApi' :: Proxy TestApi'
testApi' = Proxy

-- * Server-side handler

-- where our static files reside
www :: FilePath
www = "examples/www"

-- defining handlers
server :: TVar Counter -> Server TestApi
server counter = counterPlusOne counter     -- (+1) on the TVar
            :<|> currentValue counter       -- read the TVar

server' :: TVar Counter -> Server TestApi'
server counter = server counter
            :<|> serveDirectory www         -- serve static files

runServer :: TVar Counter -- ^ shared variable for the counter
          -> Int          -- ^ port the server should listen on
          -> IO ()
runServer var port = run port (serve testApi' $ server' var)

main :: IO ()
main = do
  -- write the JS code to www/api.js at startup
  writeJSForAPI testApi jquery (www </> "api.js")

  -- setup a shared counter
  cnt <- newCounter

  -- listen to requests on port 8080
  runServer cnt 8080