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.