Using Express web framework

Interoperating with JavaScript libraries

This demo uses Fable to create a web application using Express, a fast, unopinionated, minimalist web framework for Node.js. You can see full source code here, or view the raw source on GitHub. The application configuration is in package.json and fableconfig.json specifies Fable parameters.

Using Express bindings

Fable comes with bindings for a number of sample node.js libraries including Express. You can view and contribute to it on GitHub. The following references the Express bindings:

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
#r "node_modules/fable-core/Fable.Core.dll"
#I "node_modules/fable-import-express"
#load "Fable.Import.Express.fs"

open System
open Fable.Core
open Fable.Core.JsInterop
open Fable.Import

Defining Express bindings

For detailed documentation on defining Fable bindings, see the Interacting with JavaScript page. We won't cover all the details, but we briefly look at two snippets from the Fable.Import.Express.fs file on GitHub.

The first snippet defines the Express type. In JavaScript, this is a callable function, which is mapped to a method with a special Emit attribute in F#:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
type Express =
  inherit Application
  abstract version: string with get, set
  abstract mime: string with get, set
  abstract application: obj with get, set
  abstract request: Request with get, set
  abstract response: Response with get, set
  
  [<Emit("$0($1...)")>] 
  abstract Invoke: unit -> Application

The second interesting type definition defines the IRouter<'T> type. The Application object inherits from router and so once we obtain Application using express.Invoke, we will be able to call its methods to specify handlers for routes:

1: 
2: 
3: 
4: 
5: 
6: 
7: 
type IRouter<'T> =
  abstract get: name: U2<string, Regex> 
    * [<ParamArray>] handlers: RequestHandler[] -> obj
  abstract post: name: U2<string, Regex> 
    * [<ParamArray>] handlers: RequestHandler[] -> obj
  abstract put: name: U2<string, Regex> 
    * [<ParamArray>] handlers: RequestHandler[] -> obj

One interesting thing here is that get, put and post can take either a regular expression or a string. In JavaScript, you just pass the object to the function. In F#, this is mapped to a simple two-case discriminated union, so that you get static checking. We'll see how to use these mappings next.

Calling Express bindings

The application itself is now very simple. We call express.Invoke to get an instance of the Express server, we specify one sample binding for GET request using app.get, we get a port for our server and start it:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
let app = express.Invoke()

// Handle request using plain `string` route specification
app.get
  ( U2.Case1 "/hello/:name", 
    fun (req:express.Request) (res:express.Response) _ ->
      res.send(sprintf "Hello %O" req.``params``?name) |> box)
|> ignore

// Get PORT environment variable or use default
let port =
  match unbox Node.``process``.env?PORT with
  | Some x -> x | None -> 8080

// Start the server on the port
app.listen(port, unbox (fun () ->
  printfn "Server started: http://localhost:%i/" port))
|> ignore

Note that when calling app.get, the first parameter is a plain string value and so we wrap it using U2.Case1. If you wanted to specify a regular expression, you'd use a RegEx object wrapped in U2.Case2.

namespace System
namespace Fable
namespace Fable.Core
module JsInterop

from Fable.Core
namespace Fable.Import
type Express =
  interface
    inherit obj
    abstract member Invoke : unit -> 'a0
    abstract member application : obj
    abstract member mime : string
    abstract member request : 'a0
    abstract member response : 'a0
    abstract member version : string
    abstract member application : obj with set
    abstract member mime : string with set
    abstract member request : 'a0 with set
    ...
  end

Full name: index.Express
abstract member Express.version : string with set

Full name: index.Express.version
Multiple items
val string : value:'T -> string

Full name: Microsoft.FSharp.Core.Operators.string

--------------------
type string = System.String

Full name: Microsoft.FSharp.Core.string
val set : elements:seq<'T> -> Set<'T> (requires comparison)

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.set
abstract member Express.mime : string with set

Full name: index.Express.mime
abstract member Express.application : obj with set

Full name: index.Express.application
type obj = System.Object

Full name: Microsoft.FSharp.Core.obj
abstract member Express.request : 'a0 with set

Full name: index.Express.request
abstract member Express.response : 'a0 with set

Full name: index.Express.response
abstract member Express.Invoke : unit -> 'a0

Full name: index.Express.Invoke
type unit = Unit

Full name: Microsoft.FSharp.Core.unit
type IRouter<'T> =
  interface
    abstract member get : name:'a1 * handlers:'a2 [] -> obj
    abstract member post : name:'a1 * handlers:'a2 [] -> obj
    abstract member put : name:'a1 * handlers:'a2 [] -> obj
  end

Full name: index.IRouter<_>
abstract member IRouter.get : name:'a1 * handlers:'a2 [] -> obj

Full name: index.IRouter`1.get
abstract member IRouter.post : name:'a1 * handlers:'a2 [] -> obj

Full name: index.IRouter`1.post
abstract member IRouter.put : name:'a1 * handlers:'a2 [] -> obj

Full name: index.IRouter`1.put
val app : express.Express

Full name: Index.app
Multiple items
val express : express.Globals

Full name: Fable.Import.express_Extensions.express

--------------------
module express

from Fable.Import
member express.Globals.Invoke : unit -> express.Express
abstract member express.IRouter.get : name:U2<string,Text.RegularExpressions.Regex> * [<ParamArray>] handlers:express.RequestHandler [] -> obj
type U2<'a,'b> =
  | Case1 of 'a
  | Case2 of 'b

Full name: Fable.Core.U2<_,_>
union case U2.Case1: 'a -> U2<'a,'b>
val req : express.Request
type Request =
  interface
    inherit Request
    inherit ServerRequest
    abstract member accepts : type:string -> string
    abstract member accepts : type:ResizeArray<string> -> string
    abstract member acceptsCharsets : ?charset:U2<string,ResizeArray<string>> -> ResizeArray<string>
    abstract member acceptsEncodings : ?encoding:U2<string,ResizeArray<string>> -> ResizeArray<string>
    abstract member acceptsLanguages : ?lang:U2<string,ResizeArray<string>> -> ResizeArray<string>
    abstract member clearCookie : name:string * ?options:obj -> Response
    abstract member get : name:string -> string
    abstract member accepted : ResizeArray<MediaType>
    ...
  end

Full name: Fable.Import.express.Request
val res : express.Response
type Response =
  interface
    inherit Response
    inherit ServerResponse
    abstract member attachment : ?filename:string -> Response
    abstract member clearCookie : name:string * ?options:obj -> Response
    abstract member contentType : type:string -> Response
    abstract member cookie : name:string * val:string * options:CookieOptions -> Response
    abstract member cookie : name:string * val:obj * options:CookieOptions -> Response
    abstract member cookie : name:string * val:obj -> Response
    abstract member download : path:string -> unit
    abstract member download : path:string * filename:string -> unit
    ...
  end

Full name: Fable.Import.express.Response
abstract member express.Response.send : body:obj -> express.Response
abstract member express.Response.send : status:float * ?body:obj -> express.Response
val sprintf : format:Printf.StringFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.sprintf
val box : value:'T -> obj

Full name: Microsoft.FSharp.Core.Operators.box
val ignore : value:'T -> unit

Full name: Microsoft.FSharp.Core.Operators.ignore
val port : int

Full name: Index.port
val unbox : value:obj -> 'T

Full name: Microsoft.FSharp.Core.Operators.unbox
module Node

from Fable.Import
union case Option.Some: Value: 'T -> Option<'T>
val x : int
union case Option.None: Option<'T>
abstract member express.Application.listen : port:float * ?callback:JS.Function -> Node.http_types.Server
abstract member express.Application.listen : path:string * ?callback:JS.Function -> Node.http_types.Server
abstract member express.Application.listen : handle:obj * ?listeningListener:JS.Function -> Node.http_types.Server
abstract member express.Application.listen : port:float * hostname:string * ?callback:JS.Function -> Node.http_types.Server
abstract member express.Application.listen : port:float * hostname:string * backlog:float * ?callback:JS.Function -> Node.http_types.Server
val printfn : format:Printf.TextWriterFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
Fork me on GitHub