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
packages.json and
fableconfig.json
specifies Fable parameters.
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:
|
#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.Import
|
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 yo uget static checking. We'll see how to use these mappings next.
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
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.Server
abstract member express.Application.listen : path:string * ?callback:JS.Function -> Node.http.Server
abstract member express.Application.listen : handle:obj * ?listeningListener:JS.Function -> Node.http.Server
abstract member express.Application.listen : port:float * hostname:string * ?callback:JS.Function -> Node.http.Server
abstract member express.Application.listen : port:float * hostname:string * backlog:float * ?callback:JS.Function -> Node.http.Server
val printfn : format:Printf.TextWriterFormat<'T> -> 'T
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn