Requests & Responses
Learn how to handle different request types and build proper responses in Suave.
HTTP Response Types
Suave provides helpers for common HTTP responses:
open Suave
open Suave.Successful
open Suave.RequestErrors
open Suave.ServerErrors
open Suave.Filters
open Suave.Operators
let app =
choose [
path "/ok" >=> OK "200 OK"
path "/created" >=> CREATED "201 Created"
path "/accepted" >=> ACCEPTED "202 Accepted"
path "/bad-request" >=> BAD_REQUEST "400 Bad Request"
path "/unauthorized" >=> UNAUTHORIZED "401 Unauthorized"
path "/forbidden" >=> FORBIDDEN "403 Forbidden"
path "/not-found" >=> NOT_FOUND "404 Not Found"
path "/conflict" >=> CONFLICT "409 Conflict"
path "/server-error" >=> INTERNAL_ERROR "500 Server Error"
path "/not-implemented" >=> NOT_IMPLEMENTED "501 Not Implemented"
]
Reading Request Headers
Access HTTP headers from the request:
open Suave
open Suave.Filters
open Suave.Operators
open Suave.Successful
let app =
choose [
path "/headers" >=> fun ctx ->
let userAgent =
match ctx.request.header "user-agent" with
| Choice1Of2 ua -> sprintf "User-Agent: %s" ua
| Choice2Of2 _ -> "User-Agent not provided"
let contentType =
match ctx.request.header "content-type" with
| Choice1Of2 ct -> sprintf "Content-Type: %s" ct
| Choice2Of2 _ -> "Content-Type not provided"
let response = sprintf "%s\n%s" userAgent contentType
OK response ctx
]
Query String Parameters
Extract and parse query parameters:
open Suave
open Suave.Filters
open Suave.Operators
open Suave.Successful
open Suave.RequestErrors
let app =
choose [
path "/search" >=> fun ctx ->
match ctx.request.queryParam "q" with
| Choice1Of2 query ->
OK (sprintf "Search results for: %s" query) ctx
| Choice2Of2 _ ->
BAD_REQUEST "Missing 'q' parameter" ctx
path "/page" >=> fun ctx ->
match ctx.request.queryParam "n" with
| Choice1Of2 nStr when System.Int32.TryParse(nStr) |> fst ->
let page = System.Int32.Parse(nStr)
OK (sprintf "Page: %d" page) ctx
| _ ->
BAD_REQUEST "Invalid page number" ctx
]
Request Body (Form Data)
Read form data from POST requests:
open Suave
open Suave.Filters
open Suave.Operators
open Suave.Successful
open Suave.RequestErrors
let app =
choose [
POST >=> path "/form" >=> fun ctx ->
async {
match ctx.request.formData "name" with
| Choice1Of2 name ->
let response = sprintf "Hello, %s!" name
return! OK response ctx
| Choice2Of2 _ ->
return! BAD_REQUEST "Missing 'name' field" ctx
}
]
JSON Request/Response
Work with JSON using the Suave.Json package (dotnet add package Suave.Json). toJson/fromJson use DataContractJsonSerializer, so mark records with DataContract:
open System.Runtime.Serialization
open Suave
open Suave.Filters
open Suave.Operators
open Suave.Successful
open Suave.RequestErrors
open Suave.Writers
open Suave.Json
[<DataContract>]
type User =
{ [<field: DataMember(Name = "id")>] id : int
[<field: DataMember(Name = "name")>] name : string
[<field: DataMember(Name = "email")>] email : string }
let app =
choose [
POST >=> path "/api/users" >=> fun ctx ->
try
let user = fromJson<User> ctx.request.rawForm
OK (sprintf "Created user: %s" user.name) ctx
with
| _ -> BAD_REQUEST "Invalid JSON" ctx
GET >=> path "/api/users" >=> fun ctx ->
let users =
[ { id = 1; name = "Alice"; email = "alice@example.com" }
{ id = 2; name = "Bob"; email = "bob@example.com" } ]
(toJson users |> Successful.ok >=> setMimeType "application/json") ctx
]
Setting Response Headers
Add custom headers to responses:
open Suave
open Suave.Filters
open Suave.Operators
open Suave.Successful
open Suave.Writers
let app =
choose [
path "/custom-headers" >=>
setHeader "X-Custom-Header" "CustomValue" >=>
setHeader "Cache-Control" "no-cache" >=>
OK "Response with custom headers"
path "/json-api" >=>
setMimeType "application/json; charset=utf-8" >=>
OK "{\"message\":\"Hello\"}"
]
Setting Response Cookies
Add cookies to responses:
open Suave
open Suave.Writers
open Suave.Filters
open Suave.Operators
open Suave.Successful
let app =
choose [
path "/set-cookie" >=>
setHeader "Set-Cookie" "session=abc123; Path=/" >=>
OK "Cookie set"
]
Complete CRUD Example
A complete example with multiple request types:
open Suave
open Suave.Operators
open Suave.Filters
open Suave.Successful
open Suave.RequestErrors
type Product = { id: int; name: string; price: float }
let mutable products =
[ { id = 1; name = "Widget"; price = 9.99 }
{ id = 2; name = "Gadget"; price = 19.99 } ]
let app =
choose [
// GET /products - List all
GET >=> path "/products" >=>
OK (sprintf "Products: %A" products)
// GET /products/:id - Get one
GET >=> pathScan "/products/%d" (fun id ->
match products |> List.tryFind (fun p -> p.id = id) with
| Some p -> OK (sprintf "Product: %s - $%.2f" p.name p.price)
| None -> NOT_FOUND "Product not found"
)
// POST /products - Create
POST >=> path "/products" >=> fun ctx ->
match ctx.request.formData "name", ctx.request.formData "price" with
| Choice1Of2 name, Choice1Of2 priceStr when System.Double.TryParse(priceStr) |> fst ->
let price = System.Double.Parse(priceStr)
let id = products |> List.maxBy (fun p -> p.id) |> fun p -> p.id + 1
let product = { id = id; name = name; price = price }
products <- products @ [product]
CREATED (sprintf "Created: %s" name) ctx
| _ -> BAD_REQUEST "Invalid data" ctx
]