I've been playing around Suave for the very first time and clearly there's something I don't understand. What I'm trying to achieve is to implement a simple Rest API:
For now for simplicity I focus on GET methods only.
My very basic piece of code is here:
[<AutoOpen>]
module RestFul =
let JSON v =
let jsonSerializerSettings = new JsonSerializerSettings()
jsonSerializerSettings.ContractResolver <- new CamelCasePropertyNamesContractResolver()
JsonConvert.SerializeObject(v, jsonSerializerSettings)
|> OK
>=> Writers.setMimeType "application/json; charset=utf-8"
let fromJson<'a> json =
JsonConvert.DeserializeObject(json, typeof<'a>) :?> 'a
let getResourceFromReq<'a> (req : HttpRequest) =
let getString rawForm = System.Text.Encoding.UTF8.GetString(rawForm)
req.rawForm |> getString |> fromJson<'a>
type RestResource<'a> = {
GetById : int -> 'a option
GetPricesById : int -> 'a option
}
let rest resource =
let handleResource requestError = function
| Some r -> r |> JSON
| _ -> requestError
let getResourceById =
resource.GetById >> handleResource (NOT_FOUND "Resource not found")
let getPricesById =
resource.GetPricesById >> handleResource (NOT_FOUND "Resource not found")
choose [
GET >=> pathScan "/instrument/%d" getResourceById
GET >=> pathScan "/instrument/%d/prices" getPricesById
]
module Main =
[<EntryPoint>]
let main argv =
let webPart = rest {
GetById = fun i -> Some i // placeholder
GetPricesById = fun i -> Some i // placeholder, it'll be a list eventually
}
startWebServer defaultConfig webPart
0
When I define the WebPart in this way:
choose [
GET >=> pathScan "/instrument/%d" getResourceById // Returns the instrument static data
GET >=> pathScan "/instrument/%d/prices" getPricesById // Returns price list for the instrument
]
Then everything works fine. I'm wondering if there's a way to nest the webparts, e.g. like this:
// My idea about the code - doesn't compile
choose [
pathScan "/instrument/%d" getResourceById >=> choose [
GET // Returns the instrument static data
GET >=> path "/prices" >=> (somehow refer to the previous id and get prices) // Returns price list for the instrument
]
]
Also - as I'm learning about the RestAPIs there might be a gap in my reasoning. I think that nesting prices endpoint in that way makes it simply clear that prices are considered a property of an instrument (feel free to correct me if I'm wrong).
Right, so accessing a previous request is kind of anti suave ;) we want things to be able to happen independently regardless of what just happened. So perhaps a better way of thinking solving this would be simply appending prices to the end of the path?
choose [
GET >=> pathScan "/instrument/%d" getResourceById
GET >=> pathScan "/instrument/%d/prices" getPricesById
]