Skip to content

Instantly share code, notes, and snippets.

@konn
Last active October 4, 2020 19:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save konn/3a94165772886f8ee2fd78fd5af835b7 to your computer and use it in GitHub Desktop.
Save konn/3a94165772886f8ee2fd78fd5af835b7 to your computer and use it in GitHub Desktop.
JSaddle (Miso) and custom Backend Server, running on the same host & port
{-# LANGUAGE DuplicateRecordFields, OverloadedStrings, RecordWildCards #-}
module JSaddleCompat (runApp, App(..)) where
import qualified Language.Javascript.JSaddle.Warp as JSaddle
import Miso (startApp, syncPoint)
import qualified Miso as Miso
import Network.HTTP.Client.Conduit (Manager, newManager)
import Network.HTTP.ReverseProxy (WaiProxyResponse (..),
defaultOnExc, waiProxyTo)
import Network.Wai (Application, pathInfo)
import qualified Network.Wai.Handler.Warp as Warp
import Network.Wai.Handler.WebSockets (isWebSocketsReq)
import Network.Wai.Middleware.Rewrite (rewritePureWithQueries)
import Network.WebSockets (defaultConnectionOptions)
runApp :: Eq state => Application -> App state action -> IO ()
runApp backend misoApp = do
jSaddle <- JSaddle.jsaddleOr defaultConnectionOptions
(startApp misoApp >> syncPoint)
$ JSaddle.jsaddleApp
man <- newManager
Warp.runSettings
(Warp.setPort 9292 (Warp.setTimeout 3600 Warp.defaultSettings))
$ router WaiApps
{ jsaddle = jSaddle
, backend = backend
, manager = man
}
router :: WaiApps -> Application
router WaiApps{..} = waiProxyTo route defaultOnExc manager
where
route req = pure $
case (pathInfo req) of
[] | isWebSocketsReq req -> WPRApplication jsaddle
-- jsaddle maintains dedicated websock on /
| otherwise -> WPRApplication backend
-- otherwise we can just serve index.html from the backend.
-- we can serve genuine HTML containing CDN links etc from the backend!
("ws" : _) -> WPRApplication backend -- if backend server runs some WebSock on /ws
["static", "all.js"] -> WPRApplication
$ rewritePureWithQueries (\(_, qry) _ -> (["jsaddle.js"], qry)) jsaddle
-- jsaddle-warp always serves js on /jsaddle.js
-- we have to redirect ghcjs script link(s) to it.
("static": _) -> WPRApplication backend
-- serves static files from the backend (or wherever)
_ -> WPRApplication jsaddle
-- delegate default behaviour to jsaddle (as you like)
data WaiApps =
WaiApps
{ jsaddle :: Application
, backend :: Application
, manager :: Manager
}
@konn
Copy link
Author

konn commented Oct 4, 2020

Advantages of this approach:

  • We can safely rely on external JavaSacript libs loaded by <script> tags.
  • No need for maintaining separate server processes, especially when the backend server has to run some additional APIs (REST, WebSocket, SSE...). No conditional needed (except for switching jsaddle-warp b/w ghcjs)
  • One have the same design as the production!

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