This script is a minimal example showing how to use Fable to create a node.js HTTP server that
hosts static files from the current directory using the serve-static
package.
You can view the source code,
packages.json and
fableconfig.json on
GitHub. This page shows the full source code of the demo.
Aside from the F# source code, the directory with the
sample also contains
packages.json
and fableconfig.json
files that configure node.js dependencies and specify
parameters for the Fable compiler. In this demo, we're using the finalhandler package
and servestatic package to serve static files via HTTP.
We also need fable-core
, which contains F# mappings for core JavaScript functionality:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
|
{
"private": true,
"dependencies": {
"fable-core": "^0.0.8",
"finalhandler": "^0.4.1",
"serve-static": "^1.10.2"
},
"devDependencies": {},
"engines": {
"fable": ">=0.2.0"
}
}
|
When you compile the project by running fable
, it reads the following fableconfig.json
file.
This instructs Fable to process the index.fsx
file (alternatively, you can specify an F# project
file using .fsproj
):
1:
2:
3:
4:
5:
6:
7:
8:
9:
|
{
"module": "commonjs",
"sourceMaps": true,
"projFile": "./index.fsx",
"outDir": "out",
"scripts": {
"prebuild": "npm install"
}
}
|
This sample accesses a couple of JavaScript libraries. To do that, we need to load the
Fable core library which provides F# functions and operators for calling JavaScript.
For more information, see the Interacting with JavaScript page.
The following loads the Fable.Core.dll
from node_modules
and imports the
two node.js dependencies:
1:
2:
3:
4:
5:
6:
7:
8:
|
#r "node_modules/fable-core/Fable.Core.dll"
open System
open Fable.Core
open Fable.Import
open Fable.Import.Node
let finalhandler = require.Invoke("finalhandler")
let serveStatic = require.Invoke("serve-static")
|
The Fable.Import.Node
namespace contains mappings for global node.js objects. For example,
we can access the argv
parameters of the application and get the default port (note that
process
is a reserved keyword in F#, so we need to escape it using back-ticks):
1:
2:
3:
4:
|
let port =
match ``process``.argv with
| args when args.Count >= 3 -> int args.[2]
| _ -> 8080
|
In JavaScript, you would create static server by calling serveStatic("./")
. In Fable, the
imported serverStatic
value is not typed as a function and so we cannot call it directly.
Fable provides $
operator that lets you write untyped function invocations.
The following implements the server and listens on the specified port
:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
|
let server =
let serve = serveStatic $ "./"
let server =
// As this lambda has more than one argument, it must be
// converted to delegate so it's called back correctly
http.createServer(Func<_,_,_>(fun req res ->
let isDone = finalhandler $ (req, res)
serve $ (req, res, isDone)
|> ignore))
server.listen port
|
To report that the server is running, we can use System.Console.WriteLine
from the
.NET library, but Fable also provides support for F# printfn
function:
1:
|
printfn "Server running at localhost:%i" port
|
namespace System
namespace Fable
namespace Fable.Core
namespace Fable.Import
module Node
from Fable.Import
val finalhandler : obj
Full name: Index.finalhandler
val require : NodeRequire
Full name: Fable.Import.Node.require
abstract member NodeRequireFunction.Invoke : id:string -> obj
val serveStatic : obj
Full name: Index.serveStatic
val port : int
Full name: Index.port
val args : ResizeArray<string>
property Collections.Generic.List.Count: int
Multiple items
val int : value:'T -> int (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.int
--------------------
type int = int32
Full name: Microsoft.FSharp.Core.int
--------------------
type int<'Measure> = int
Full name: Microsoft.FSharp.Core.int<_>
val server : http.Server
Full name: Index.server
val serve : obj
val server : http.Server
Multiple items
val http : http.Globals
Full name: Fable.Import.Node.http
--------------------
module http
from Fable.Import.Node
member http.Globals.createServer : ?requestListener:Func<http.IncomingMessage,http.ServerResponse,unit> -> http.Server
Multiple items
type Func<'TResult> =
delegate of unit -> 'TResult
Full name: System.Func<_>
--------------------
type Func<'T,'TResult> =
delegate of 'T -> 'TResult
Full name: System.Func<_,_>
--------------------
type Func<'T1,'T2,'TResult> =
delegate of 'T1 * 'T2 -> 'TResult
Full name: System.Func<_,_,_>
--------------------
type Func<'T1,'T2,'T3,'TResult> =
delegate of 'T1 * 'T2 * 'T3 -> 'TResult
Full name: System.Func<_,_,_,_>
--------------------
type Func<'T1,'T2,'T3,'T4,'TResult> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 -> 'TResult
Full name: System.Func<_,_,_,_,_>
--------------------
type Func<'T1,'T2,'T3,'T4,'T5,'TResult> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 -> 'TResult
Full name: System.Func<_,_,_,_,_,_>
--------------------
type Func<'T1,'T2,'T3,'T4,'T5,'T6,'TResult> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 -> 'TResult
Full name: System.Func<_,_,_,_,_,_,_>
--------------------
type Func<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'TResult> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 -> 'TResult
Full name: System.Func<_,_,_,_,_,_,_,_>
--------------------
type Func<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'TResult> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 -> 'TResult
Full name: System.Func<_,_,_,_,_,_,_,_,_>
--------------------
type Func<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'TResult> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 -> 'TResult
Full name: System.Func<_,_,_,_,_,_,_,_,_,_>
--------------------
type Func<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'TResult> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 -> 'TResult
Full name: System.Func<_,_,_,_,_,_,_,_,_,_,_>
--------------------
type Func<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'TResult> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 -> 'TResult
Full name: System.Func<_,_,_,_,_,_,_,_,_,_,_,_>
--------------------
type Func<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12,'TResult> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 * 'T12 -> 'TResult
Full name: System.Func<_,_,_,_,_,_,_,_,_,_,_,_,_>
--------------------
type Func<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12,'T13,'TResult> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 * 'T12 * 'T13 -> 'TResult
Full name: System.Func<_,_,_,_,_,_,_,_,_,_,_,_,_,_>
--------------------
type Func<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12,'T13,'T14,'TResult> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 * 'T12 * 'T13 * 'T14 -> 'TResult
Full name: System.Func<_,_,_,_,_,_,_,_,_,_,_,_,_,_,_>
--------------------
type Func<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12,'T13,'T14,'T15,'TResult> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 * 'T12 * 'T13 * 'T14 * 'T15 -> 'TResult
Full name: System.Func<_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_>
--------------------
type Func<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12,'T13,'T14,'T15,'T16,'TResult> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 * 'T12 * 'T13 * 'T14 * 'T15 * 'T16 -> 'TResult
Full name: System.Func<_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_>
val req : http.IncomingMessage
val res : http.ServerResponse
val isDone : obj
val ignore : value:'T -> unit
Full name: Microsoft.FSharp.Core.Operators.ignore
abstract member http.Server.listen : handle:obj * ?listeningListener:JS.Function -> http.Server
abstract member http.Server.listen : path:string * ?callback:JS.Function -> http.Server
abstract member http.Server.listen : port:float * ?hostname:string * ?callback:JS.Function -> http.Server
abstract member http.Server.listen : port:float * ?hostname:string * ?backlog:float * ?callback:JS.Function -> http.Server
val printfn : format:Printf.TextWriterFormat<'T> -> 'T
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn