I have an f# chat application that needs rest apis exposed as well as have websockets for real time messaging. I am using Suave framework.
I have a frontend which has a "Chat" button that runs javascript on click. The javascript triggers the creates a web socket for the websocket url (/websocket) and establishes a connection for the real time chats. However, I have another button "FetchUsers" for fetching all the users in the system. But for this, I will need a different path (/people). I dont think I can use the (/websocket) path because in the backend, there is only one function which can be defined for a particular path.
To achieve this I have come up with the idea that I could expose 2 different ports : one for the rest api and one for the web sockets. My rest api (localhost:8082/people) is used to get all the users in the system and the web sockets (localhost:8080/websocket) is for sending chat messages to all users in the system
I am open to any other suggestions as to how can I implement the chat application.
Program.fs
let app : WebPart =
choose [
path "/websocket" >=> handShake ws
path "/websocketWithSubprotocol" >=> handShakeWithSubprotocol (chooseSubprotocol "test") ws
path "/websocketWithError" >=> handShake wsWithErrorHandling
GET >=> choose [ path "/" >=> file "index.html"; browseHome ]
NOT_FOUND "Found no handlers." ]
let myCfg =
{ defaultConfig with
bindings = [
HttpBinding.createSimple HTTP "127.0.0.1" 8080
HttpBinding.createSimple HTTP "127.0.0.1" 8082
]
}
[<EntryPoint>]
let main _ =
let personWebPart = rest "people" {
GetAll = Db.getPeople
Create = Db.createPerson
}
startWebServer myCfg personWebPart
startWebServer myCfg app
Restful.fs
module RestFul =
open Suave.Web
open Suave.Successful
open Newtonsoft.Json
open Suave
open Suave.Operators
open Suave.Filters
open Suave.Files
open Suave.RequestErrors
open Newtonsoft.Json.Serialization
type RestResource<'a> = {
GetAll : unit -> 'a seq
Create : 'a -> 'a
}
let fromJson<'a> json =
JsonConvert.DeserializeObject(json, typeof<'a>) :?> 'a
let getResourceFromReq<'a> (req : HttpRequest) =
let getString (rawForm: byte[]) =
System.Text.Encoding.UTF8.GetString(rawForm)
req.rawForm |> getString |> fromJson<'a>
let JSON v =
let jsonSerializerSettings = JsonSerializerSettings()
jsonSerializerSettings.ContractResolver <- CamelCasePropertyNamesContractResolver()
JsonConvert.SerializeObject(v, jsonSerializerSettings)
|> OK
>=> Writers.setMimeType "application/json; charset=utf-8"
let rest resourceName resource =
let resourcePath = "/" + resourceName
let getAll = warbler (fun _ -> resource.GetAll () |> JSON)
path resourcePath >=> choose [
GET >=> getAll
POST >=> request (getResourceFromReq >> resource.Create >> JSON)
]
How can assign the websocket to port 8080 and rest api to port 8082? I am doing this for now. The server starts up on the ports but I am only able to access the rest url ie localhost:8080/people
startWebServer myCfg personWebPart
startWebServer myCfg app
Reiterating my previous point, I would like to know any other approaches as to how can I implement the chat application.
It has been awhile since I've used Suave but if I recall it has WebSocket support using a custom "socket" computation expression. You loop in the expression, wait for a message, and then handle it. Note that waiting on the Websocket is in itself an async operation and that each client has their own loop.
Example is on their Github: https://github.com/SuaveIO/suave/blob/master/examples/WebSocket/Program.fs