Static HTTP server

Minimal demo using node.js to host static files

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, package.json and fableconfig.json on GitHub. This page shows the full source code of the demo.

Configuring Fable and packages

Aside from the F# source code, the directory with the sample also contains package.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"
    }
}

Referencing Fable and dependencies

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: 
9: 
#r "node_modules/fable-core/Fable.Core.dll"
open System
open Fable.Core
open Fable.Core.JsInterop
open Fable.Import
open Fable.Import.Node

let finalhandler = importDefault<obj> "finalhandler"
let serveStatic = importDefault<obj> "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 a 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
module JsInterop

from Fable.Core
namespace Fable.Import
module Node

from Fable.Import
val finalhandler : obj

Full name: Index.finalhandler
val importDefault : path:string -> 'T

Full name: Fable.Core.JsInterop.importDefault
type obj = Object

Full name: Microsoft.FSharp.Core.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_types.Server

Full name: Index.server
val serve : obj
val server : http_types.Server
val http : http_types.Globals

Full name: Fable.Import.Node.http
member http_types.Globals.createServer : ?requestListener:Func<http_types.IncomingMessage,http_types.ServerResponse,unit> -> http_types.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_types.IncomingMessage
val res : http_types.ServerResponse
val isDone : obj
val ignore : value:'T -> unit

Full name: Microsoft.FSharp.Core.Operators.ignore
abstract member http_types.Server.listen : handle:obj * ?listeningListener:JS.Function -> http_types.Server
abstract member http_types.Server.listen : path:string * ?callback:JS.Function -> http_types.Server
abstract member http_types.Server.listen : port:float * ?hostname:string * ?callback:JS.Function -> http_types.Server
abstract member http_types.Server.listen : port:float * ?hostname:string * ?backlog:float * ?callback:JS.Function -> http_types.Server
val printfn : format:Printf.TextWriterFormat<'T> -> 'T

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