Async WebParts and >=>

Every Suave handler is a WebPart: a function from HttpContext to an async optional context.

type WebPart = HttpContext -> Async<HttpContext option>

Async computation expressions

You can write WebParts with F# async workflows:

open Suave
open Suave.Successful

let sleep (milliseconds : int) message : WebPart =
  fun ctx -> async {
    do! Async.Sleep milliseconds
    return! OK message ctx
  }

Kleisli composition (>=>)

The >=> operator chains two WebParts. If the left-hand side returns None, the right-hand side is never run (short-circuit). If it returns Some ctx, that context is passed to the next WebPart.

open Suave
open Suave.Filters
open Suave.Operators
open Suave.Successful

let app =
  path "/hello"
  >=> GET
  >=> OK "Hello"

This is the same idea as railway-oriented programming: success continues, failure stops the chain.

choose

choose tries WebParts in order until one succeeds:

open Suave
open Suave.Filters
open Suave.Operators
open Suave.Successful

let app = choose [
  path "/a" >=> OK "A"
  path "/b" >=> OK "B"
  RequestErrors.NOT_FOUND "Not found"
]

WebParts are values — evaluate per request

A WebPart closes over values captured when it was constructed. If you bake in DateTime.Now at definition time, every request sees that same timestamp. Wrap dynamic work in a function applied to the context (or use context / request / warbler) so it runs per request. See the FAQ.

Next: operators, composition, or a recipe.