How do I send and receive data using POST?
This recipe shows how to create an endpoint on the server and hook up it up to the client using HTTP POST. This recipe assumes that you have also followed this recipe and have an understanding of MVU messaging. This recipe only shows how to wire up the client and server.
A POST endpoint is normally used to send data from the client to the server in the body, for example from a form. This is useful when we need to supply more data than can easily be provided in the URI.
You may wish to use POST for "write" operations and use GETs for "reads", however this is a highly opinionated topic that is beyond the scope of this recipe.
I'm using the standard template (Fable Remoting)
Fable Remoting takes care of deciding whether to use POST or GET etc. - you don't have to worry about this. Refer to this recipe for more details.
I'm using the minimal template (Raw HTTP)
In Shared
1. Create contract
Create the type that will store the payload sent from the client to the server.
type SaveCustomerRequest =
{ Name : string
Age : int }
On the Client
1. Call the endpoint
Create a new function saveCustomer
that will call the server. It supplies the customer to save, which
is serialized and sent to the server in the body of the message.
let saveCustomer customer =
let save customer = Fetch.post<SaveCustomerRequest, int> ("/api/customer", customer)
Cmd.OfPromise.perform save customer CustomerSaved
The generic arguments of
Fetch.post
are the input and output types. The example above shows that the input is of typeSaveCustomerRequest
with the response will contain an integer value. This may be the ID generated by the server for the save operation.
This can now be called from within your update
function e.g.
| SaveCustomer request ->
model, saveCustomer request
| CustomerSaved generatedId ->
{ model with GeneratedCustomerId = Some generatedId; Message = "Saved customer!" }, Cmd.none
On the Server
1. Write implementation
Create a function that can extract the payload from the body of the request using Giraffe's built-in model binding support:
open FSharp.Control.Tasks
open Giraffe
open Microsoft.AspNetCore.Http
open Shared
/// Extracts the request from the body and saves to the database.
let saveCustomer next (ctx:HttpContext) = task {
let! customer = ctx.BindModelAsync<SaveCustomerRequest>()
do! Database.saveCustomer customer
return! Successful.OK "Saved customer" next ctx
}
2. Expose your function
Tie your function into the router, using the post
verb instead of get
.
let webApp = router {
post "/api/customer" saveCustomer // Add this
}