Skip to content

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 type SaveCustomerRequest 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
}