SAFE Stack

Created: Overview of the SAFE Stack

Updated: 03 September 2023

An F# stack for everywhere, here’s the docs

Prereqs

To get started you will need:

  • .NET Core SDK 2.2
  • FAKE CLI (dotnet tool install -g fake-cli)
  • Paket (optional)
  • Node.js
  • Yarn/NPM
  • An F# IDE

Getting Started

  1. Install the FAKE CLI with
1
dotnet tool install -g fake-cli
  1. Install the SAFE template with
1
dotnet new -i SAFE.Template
  1. Create a new SAFE project
1
mkdir safe-intro
2
cd safe-intro
3
dotnet new SAFE

Aside from the default template above you can create a template with a few different choices around the server, communication, and package manager, get help on this here or with:

1
dotnet new SAFE --help
  1. Run the application
1
fake build --target run

If you’re using VSCode the relevant configs are already there for you to use under the debug options

The Stack

The Safe Stack consists of the following technologies

  • Saturn for backend F# services, based on .NET Core and the Giraffe Library. Giraffe can also be unsed directly in place of Saturn. Additionally Freya can be used
  • Azure for hosting, well also Docker or k8s or whatever really
  • Fable for running F# in Browser, F# to JS compiler powered by Babel
  • Elmish for client-side UI, based on React

Code Sharing

SAFE makes use of code sharing between the client and the server, this includes shared types as well as behaviour (such as validation)

Messages are sent between the client and server using a few different methods:

  • HTTP with Saturn
  • Contracts via Fable Remoting
  • Stateful servers with Elmish Bridge

Code that is shared between the client and server is contianed in the Shared.fs file and is referenced in the client and server

Communication

HTTP

First, create a customer type in your Shared.fs file

1
type Customer = { Id:int ; Name:string }

Then create a function that will load a customer

1
let loadCustomersFromDb() =
2
[ { Id = 1; Name = "John Smith" } ]

Thereafter create an endpoint handler which will return the customer with the json function within the Giraffe HTTP context

1
let getCustomers next ctx =
2
json (loadCustomersFromDb()) next ctx

Next you will need to expose the endpoint through Saturn’s router

First, define the router

1
let customerApi = router {
2
get "/api/customers" getCustomers
3
}

This can then be added to the app by adding the following into the app builder definition

1
use_router customerApi