valderman / haste-compiler

A GHC-based Haskell to JavaScript compiler
http://haste-lang.org
BSD 3-Clause "New" or "Revised" License
1.45k stars 109 forks source link

Parameterize runApp using environment variables. #341

Closed misha closed 9 years ago

misha commented 9 years ago

I'd like to deploy a Haste application to a PaaS which requires reading the PORT environment variable:

import System.Environment (lookupEnv)
import Data.Maybe (maybe)

getPort :: IO Int
getPort = lookupEnv "PORT" >>= return . maybe 8080 read

Unfortunately, the documentation for runApp states:

Note that runApp is single-entry, and that its argument must not depend on any external IO.

Anyway, what are my options? I'll eventually want to do something similar with the host name parameter as well.

valderman commented 9 years ago

Since the client needs to know which port and host to connect to and the server binary is the party that is able to read environment variables, this is not really possible in a clean way. If the PaaS provides some means for the JS to read those environment variables (making a request to some URL with an API key or whatnot), you might do the following:

getPort :: IO Int
#ifdef __HASTE__
getPort = getPortOnClientSideSomehow
#else
getPort = lookupEnv "PORT" >>= return . maybe 8080 read
#endif

If not, you could provide this means yourself through a wrapper script. wrapper.sh:

#!/bin/sh
PORT=$1
# 
echo "$(cat client.js) window.theport = $PORT;" > client-with-port.js
server-binary $PORT

app.hs:

{-# LANGUAGE OverloadedStrings #-}
import Haste.Foreign
thePort :: Int
thePort = constant "window.theport"

main = ...

It's not pretty, but it does the trick.

By modifying the Haste.App library slightly and perhaps adding a special compilation mode to Haste, we could pack a small web server, along with the JS itself, into the server binary and have that serve the client JS, which would make it possible to solve this problem in a nicer way.

misha commented 9 years ago

Honestly, both of those work. The PaaS compiles my application on the spot anyway so I technically do have access to it at compile time. I also have predeploy hooks that could easily run a script like that. Thanks for your feedback!