Using Express.js with Purescript
Express.js is a minimalist Javascript web framework for Node.js.
Developing Javascript applications can be frustrating due to runtime exceptions from dynamic typing. You can use Flow or Typescript to help gain type safety, I still feel this is not enough compared to PureScript using a pure functional approach with an expressive type system.
PureScript is a small strongly, statically typed compile-to-JS language with a number of interesting features, such as:
- Type Inference
- Higher Kinded Polymorphism
- Support for basic Javascript types
- Extensible records
- Extensible effects
- Optimizer rules for generation of efficient Javascript
- Pattern matching
- Simple FFI
- Modules
- Rank N Types
- Do Notation
- Tail-call elimination
- Type Classes
Below I will demonstrate a basic Hello World Express.js based application built using PureScript.
Install PureScript
npm install -g purescript
Install Pulp
Pulp is a PureScript built tool. You can use Gulp instead if you are using the Gulp already with the gulp-purescript plugin. Pulp is generally the preffered tool for PureScript.
npm install -g pulp
Install Bower
PureScript dependencies are manged with Bower
npm install -g bower
Create a new project
mkdir myproject
cd myproject
pulp init
Install the Express.js Node.js dependency
npm install express --save
Install the required Purescript libraries for the project
bower install purescript-console --save
bower install purescript-express --save
bower install purescript-errors --save
bower install purescript-node-process --save
Edit src/Main.purs
Open the skeleton src/Main.purs file and replace with the below:
module Main where
import Prelude
import Control.Apply ((*>))
import Control.Error.Util
import Control.Monad.Eff
import Control.Monad.Eff.Console (log, error)
import Data.Either
import Data.Int as I
import Data.Maybe
import Node.Express.App
import Node.Express.Handler
import Node.Express.Request
import Node.Express.Response
import Node.Process
-- Get port from environment and validate
getPort :: forall eff. Eff (process :: PROCESS | eff ) (Either String Int)
getPort = asInt <$> required <$> lookupEnv "PORT"
where
asInt :: Either String String -> Either String Int
asInt value = value >>= I.fromString >>> note "PORT must be an INT"
required :: Maybe String -> Either String String
required = note "Missing PORT env variable"
indexHandler :: forall e. Handler e
indexHandler = do
name <- getQueryParam "name"
case name of
Just n -> sendJson { msg: "Hello " ++ n }
Nothing -> sendJson { msg: "Hello World" }
-- Router config
appSetup :: forall e. App e
appSetup = do
get "/" indexHandler
-- Main entry point
main = do
port <- getPort
case port of
Left e -> error e *> exit 1
Right p -> void $ listenHttp appSetup p \_ -> log $ "Listening on " ++ show p
The basic structure of the program is:
- Get the port number to run the app on
- If port is not valid log an error
e
to stdout and then exit - If port is valid start a server with the routes defined in appSetup, on start log a message to stdout
Build the project
pulp build -O --to output.js
The -O
optimise option removes dead code.
Run the app
NODE_ENV=production PORT=8080 node output.js
You should see the below message:
Listening on 8080
Test the app
$ curl http://localhost:8080
{"msg":"Hello World"}
More examples and documentation can be found at https://github.com/dancingrobot84/purescript-express.