Suave Fable and Marten

Sandeep Chandra
2 min readApr 2, 2017

--

In this blog we’ll see how to use Marten with Suave and Fable template. Marten enables PostgreSql to be used as Document Database.

We will use Visual Studio Code with Ionide extension.

Download the fable-suave template and extract it to a folder.

In VS Code Press F1 and type Paket, this will list paket related commands, pick “Paket: Add NuGet Package”, then type in “Marten” and press enter. This will add all the project dependencies. Now we will add Marten to server side project, to do this open file src/Server/paket.references and add “Marten” at the end.

Add a new file to server project and call it “Database.fs”. Open server.fsproj and add “<Compile Include=”Database.fs” />” as the first item in section “<ItemGroup>”. Open “Database.fs” and add following code to it

module Databaseopen Systemopen Martenopen System.Linqtype User = {    Id : Guid    Username : string    Password : string}[<Literal>]let ConnecitonString = "host=localhost;database=MartenDb;username=me;password=topSecret"let store = DocumentStore.For(fun x ->x.AutoCreateSchemaObjects <- AutoCreate.CreateOrUpdatex.Connection(ConnecitonString)x.Schema.For<User>().Index(fun x -> x.Username :> obj) |> ignore)

This initializes “store” which we will use later to run queries.

We have a type called “User” which has field “Id”, this is required by Marten. More information about identity can be found here.

Create a PostgreSQL database user and a database called MartenDb with this new user as the owner.

Now let us replace the login function in “Auth.fs” as show below

let login (ctx: HttpContext) = async {
let login =
ctx.request.rawForm
|> System.Text.Encoding.UTF8.GetString
|> JsonConvert.DeserializeObject<Domain.Login>
use session = store.QuerySession()
let user = session.Query<User>().Where(fun x -> x.Username = login.UserName && x.Password = login.Password)
if user.Count() = 0 then
return! UNAUTHORIZED (sprintf "Invalid username or password.") ctx
else
let user : ServerTypes.UserRights = { UserName = login.UserName }
let token = TokenUtils.encode user
return! Successful.OK token ctx
}

We will add signup function to “Auth.fs” as shown below

let signup(ctx : HttpContext) = async {
let signup =
ctx.request.rawForm
|> System.Text.Encoding.UTF8.GetString
|> JsonConvert.DeserializeObject<Domain.Signup>
use session = store.OpenSession()
let user = session.Query<User>().Where(fun x -> x.Username = signup.Username)
if user.Count() <> 0 then
return! CONFLICT ("That username is taken. Please try another one.") ctx
else
let newUser = { Id = Guid.NewGuid(); Username = signup.Username; Password = signup.Password }
session.Store newUser
session.SaveChangesAsync() |> ignore
return! Successful.OK (TokenUtils.encode "") ctx
}

Add type Signup to “Domain.fs”

type Signup = {
Username : string
Password : string
ConfirmPassword : string
}

Lets build and run the solution to see what it looks like. Open command prompt and go to main project folder and run “build run”. This builds server and client projects and starts the browser. Refresh the page if it is not loaded. Go to login page and try logging in. User authentication will fail as this user is not in the database. Now have a look at database to see that a table to store user data has been created.

As an exercise add a signup page in client project and use the signup function in “Auth.fs” to register a new user.

The code for the project can be found on github.

--

--