FAQ
Common questions from Stack Overflow and the community. For copy-paste how-tos, see Recipes.
Why does my WebPart always return the same value?
WebParts are functions, but many combinators (OK, string interpolation of the time, etc.) capture values when the WebPart is constructed—often once at startup. Rebuild the response inside the request:
open System
open Suave
open Suave.Successful
open Suave.Operators
// Wrong: time baked in once
let bad = OK (string DateTime.UtcNow)
// Right: evaluate per request
let good : WebPart =
fun ctx -> OK (string DateTime.UtcNow) ctx
// or
let good2 = context (fun _ -> OK (string DateTime.UtcNow))File uploads are empty — request.files has nothing
The HTML form (or client) must use enctype="multipart/form-data". Without it the browser posts fields as URL-encoded text and Suave will not populate file parts.
How do >=>, choose, and <|> relate?
>=> runs A then B if A succeeded. choose tries a list until one succeeds. <|> is “try left, else right”. See Async & >=> and Operators.
How do I enable HTTPS?
Load a PKCS certificate and create an HTTPS binding:
open System.Security.Cryptography.X509Certificates
open Suave
let cert = new X509Certificate2("certificate.p12", "password")
let https = HttpBinding.createSimple (HTTPS cert) "0.0.0.0" 8443
let cfg = { defaultConfig with bindings = [ https ] }See the HTTPS recipe.
How do I redirect HTTP to HTTPS?
Inspect ctx.request.binding.scheme.secure or, behind a reverse proxy, the x-forwarded-proto header, then Redirection.redirect to an https:// URL. Recipe: HTTPS.
Can REST and WebSockets share one port?
Yes. Put both in one choose tree; you do not need two startWebServer calls.
open Suave
open Suave.Filters
open Suave.Operators
open Suave.Successful
open Suave.WebSocket
open Suave.Sockets
open Suave.Sockets.Control
let wsHandler (webSocket : WebSocket) (ctx : HttpContext) = socket {
let mutable loop = true
while loop do
let! msg = webSocket.read()
match msg with
| (Close, _, _) -> loop <- false
| _ -> ()
}
let app = choose [
path "/ws" >=> handShake wsHandler
path "/api/people" >=> GET >=> OK "..."
]How do I broadcast to all WebSocket clients?
Suave does not ship a bus. Register sockets on connect (e.g. ConcurrentDictionary or a MailboxProcessor) and iterate when sending. See broadcast recipe.
pathScan "/%s" eats the rest of my URL
pathScan matches the full remaining path against the format. Prefer formats that include the rest of the route ("/%s/customers") or nest with explicit prefixes. Recipe: Nested routes.
My app is hosted under a path prefix / behind a proxy
Ensure your path / pathScan values match what Suave sees after the proxy. Configure forwarded headers at the reverse proxy and align bindings. See Deployment.
Where is the full API documentation?
Generated reference for Suave, Suave.Json, and Suave.DotLiquid: /reference/. Module overview: Module map.