Skip to content
Write CloudFlare Workers in idiomatic, type-safe F# and compile them to JS using Fable
F#
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.paket
App
Fable.CloudFlareWorkers
assets
.gitignore
LICENSE
README.md
build.fsx
package-lock.json
package.json
paket.dependencies
paket.lock

README.md

Fable.CloudFlareWorkers

Write CloudFlare Workers in idiomatic, type-safe F# and compile them to JS using Fable

Install the CloudFlare Worker APIs

dotnet add package Fable.CloudFlareWorkers

Write your worker logic in F#

module App

open CloudFlareWorkers

let worker (request: IHttpRequest) =
    async {
        match request.method, request.path with
        | HttpMethod.GET, "/" ->
            return Response.create(body="Home, sweet home", status=200)

        | HttpMethod.POST, "/echo" ->
            let! body = request.body()
            return Response.create(body=body, status=200)

        | otherwise ->
            let body = "{ \"message\": \"Not Found\" }"
            let headers = Map.ofList [ "content-type", "application/json" ]
            return Response.create(body, status=404, headers=headers)
    }

Worker.initialize worker

A "worker" is a function of type IHttpRequest -> Async<IHttpResponse> which is what Worker.initialize expects. However, you could also implement simple synchronous workers of type IHttpRequest -> IHttpResponse and give them to Worker.initialize:

let worker (request: IHttpRequest) =
    match request.method, request.path with
    | HttpMethod.GET, "/" ->
        Response.create(body="Home, sweet home", status=200)

    | otherwise ->
        let body = "{ \"message\": \"Not Found\" }"
        let headers = [ "Content-type", "application/json" ]
        Response.create(body, status=404, headers=headers)

Worker.initialize worker

Making internal requests from the worker

CloudFlare workers are able to fetch resources using requests and receive responses. In order to send a request with fetch, you have to inside a Request Context. Using this library, you can access the request context and thus use fetch by using the type IRequestContext as your input of the worker where

type IRequestContext =
    abstract request : IHttpRequest
    abstract fetch : IHttpRequest -> Async<IHttpResponse>

Now build your worker where it expects such context:

let echo (context: IRequestContext) =
    async {
        let request = context.request
        let! response = context.fetch request
        return response
    }

Worker.initialize echo

As simple as that! Here, Worker.initialize is using another overload that has type IRequestContext -> Async<IHttpResponse>. You can also create your own requests using the Request.create function.

Compiling the project

Use a combination of fable-splitter to compile the project into ES6 syntax, then use rollup to bundle the application as a single script. First of all, assuming the directory structure is as follows:

{root}
  |
  | -- src
        |
        | -- App.fs
        | -- App.fsproj
  |
  | -- package.json
  | -- README.md
  | -- package-lock.json

Install Fable compiler along with fable-splitter and rollup:

npm install fable-compiler fable-splitter rollup @babel/core --save-dev

Then have one npm script to compile the application, another to bundle it and another that does both:

{
    "compile": "fable-splitter ./src -o ./dist",
    "bundle": "rollup ./dist/App.js -f iife --name App -o ./dist/bundle.js",
    "build": "npm run compile && npm run bundle"
}

Notice here that the entry file for rollup is ./dist/App.js that is because fable-splitter compiled the entry file App.fs as the last file from the F# project which then becomes App.js.

Now rollup takes this App.js entry file and compiles it into a single file as ./dist/bundle.js this is where your worker script lives.

As for the --name App flag of rollup, it is used to give the IIFE a name inside the file and it is required but it can be any name you want.

Testing the worker live

Copy the contents of the file ./dist/bundle.js, then go to https://cloudflareworkers.com and paste the code to the left, press CTRL + S or click the "Update" button to update the code running where the page will show you "Home, sweet home":

If you change the path in the url to something like /other then you get the "Not Found" message:

Resources

You can’t perform that action at this time.