Skip to content

Commit

Permalink
Merge pull request #323 from RicoSaupe/fixlinks
Browse files Browse the repository at this point in the history
Fixlinks
  • Loading branch information
isaacabraham authored Dec 19, 2023
2 parents b6a2dce + d1aa3b0 commit 50826e6
Show file tree
Hide file tree
Showing 40 changed files with 272 additions and 271 deletions.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ stateDiagram-v2
```

## How does Elmish integrate with SAFE?
Elmish is the library used to build the front-end application in SAFE and that application is compiled to JavaScript by [Fable](component-fable.md) to run in the browser. The [SAFE Stack template](template-overview.md) comes pre-bundled with the [Elmish React](https://elmish.github.io/react/) module, which (as the name suggests) uses the [React](https://reactjs.org/) library to handle the heavy lifting of modifyng the DOM in an efficient way. This allow us to use the pure functional style of the MVU pattern whilst still retaining the ability to have a highly performant user interface.
Elmish is the library used to build the front-end application in SAFE and that application is compiled to JavaScript by [Fable](component-fable.md) to run in the browser. The [SAFE Stack template](../template-overview.md) comes pre-bundled with the [Elmish React](https://elmish.github.io/react/) module, which (as the name suggests) uses the [React](https://reactjs.org/) library to handle the heavy lifting of modifyng the DOM in an efficient way. This allow us to use the pure functional style of the MVU pattern whilst still retaining the ability to have a highly performant user interface.

Because Elmish works alongside React, it is possible to use the vast number of available React components from the JavaScript ecosystem within our Elmish applications.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ It also provides *rich* integration with the JS ecosystem which means that you c

## How does Fable integrate with SAFE?

Fable is a tool that generates JavaScript files from F# code. This allows us to write full front end applications using F#. Being able to write both the Server and Client in the same language offers huge benefits especially when you can share code between the two, without the need for duplication. More information on code sharing can be found [here](feature-clientserver.md).
Fable is a tool that generates JavaScript files from F# code. This allows us to write full front end applications using F#. Being able to write both the Server and Client in the same language offers huge benefits especially when you can share code between the two, without the need for duplication. More information on code sharing can be found [here](../features/feature-clientserver.md).

## Fable and Vite

As Fable allows us to integrate into the JS Ecosystem, we can make use of tools such as Vite with features including [Hot Module replacement](feature-hmr.md) and Source Maps.
As Fable allows us to integrate into the JS Ecosystem, we can make use of tools such as Vite with features including [Hot Module replacement](../features/feature-hmr.md) and Source Maps.

The [SAFE Template](template-overview.md) already has Vite configured to get you up and running immediately.
The [SAFE Template](../template-overview.md) already has Vite configured to get you up and running immediately.

Learn more about Fable [here](http://fable.io/).
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Saturn provides the ability to drive your SAFE applications from the server. It
* Hosting of your client-side assets, such as HTML, CSS and JavaScript generated by Fable.
* Other cross cutting concerns e.g. authentication etc.

It also integrates with SAFE to allow seamless sharing of types and functions, since Fable will convert most F# into JavaScript. In addition, you can seamless transport data between client and server using either the Fable.JSON or Fable.Remoting libraries, both of which have support for Saturn. You can read more about this [here](feature-clientserver.md).
It also integrates with SAFE to allow seamless sharing of types and functions, since Fable will convert most F# into JavaScript. In addition, you can seamless transport data between client and server using either the Fable.JSON or Fable.Remoting libraries, both of which have support for Saturn. You can read more about this [here](../features/feature-clientserver.md).

```mermaid
flowchart TB
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ You may see the following `SocketProtocolError` message in the Debug Console onc
<center><img src="../img/feature-debugging-5.png" style="height: 175px;"/></center>

Whilst these messages can be safely ignored, you can eliminate them by installing **Redux Dev Tools** in the launched Chrome instance as described in the debugging [prerequisites](v4-recipes/developing-and-testing/debug-safe-app.md#0-install-prerequisites) section.
Whilst these messages can be safely ignored, you can eliminate them by installing **Redux Dev Tools** in the launched Chrome instance as described in the debugging [prerequisites](../v4-recipes/developing-and-testing/debug-safe-app.md#0-install-prerequisites) section.

### Node Process does not stop after stopping the VS Code debugger
VS Code does not kill the Fable process when you stop the debugger, leaving it running as a "zombie". In such a case, you will have to explicitly kill the process otherwise it will hold onto
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,72 +1,72 @@
Using F# on both client and server is at the core of the SAFE stack, as it simplifies the way we think about building web applications by using the same language, idioms and in many cases sharing our code and domain models.

However, building a client and a server app requires a fundamentally different way of thinking. On the server side we build [stateless APIs in Saturn](component-saturn.md) that map HTTP requests to internal functionality, whereas on the frontend we use the Elmish model, implementing the [model-view-update pattern](component-elmish.md): a stateful pattern that lets us think about the application state as it evolves while the application is running.

Even though we use the same language across platforms, applying these two different programming models forces us to switch our way of thinking back and forth when writing code for the client and for the server. This is where the [Elmish.Bridge](https://github.com/Nhowka/Elmish.Bridge) library comes into play: it brings the Elmish programming model *to the server* and unifies the way we write the application as a whole.

## How does Elmish work on the server?
Think of Elmish on the server as the model-view-update pattern but *without the view part*. Instead, you only need to implement `init` and `update` functions to manage the *server* state as it evolves while the server is running.

* Server state can contain data that is relevant to a single or all clients
* The dispatch loop running on the server is connected to the dispatch loop on the client via a *persistent stateful websocket connection*
* The `update` functions on client and server can exchange data via message passing.

## A simple example
Let's see a simple example of how this might work in practice:

```fsharp
// Client-side
let update msg state =
match msg with
| LoadUsers ->
// send the message to the server
state, Cmd.bridgeSend ServerMsg.LoadUsers
| UsersLoaded users ->
// receive message from the server
let nextState = { state with Users = users }
nextState, Cmd.none
// Server-side
let update clientDispatch msg state =
match msg with
| ServerMsg.LoadUsers ->
let loadUsersCmd =
Cmd.ofAsync
getUsersFromDb // unit -> Async<User list>
() // input arg = unit
UsersLoadedFromDb // User list -> ServerMsg
DoNothing // ServerMsg
state, loadUsersCmd
| ServerMsg.UsersLoadedFromDbSuccess users ->
// answer the current connected client with data
clientDispatch (ClientMsg.UsersLoaded users)
state, Cmd.none
| ServerMsg.DoNothing ->
state, Cmd.none
```

The above example mimics what would have been a `GET` request to the server to get user data from database. However, now the client sends a fire-and-forget message to the server to load users, and at some point the server messages the current client back with the results. Notice that the server could have decided to do other things than just messaging the client back: for example, it could have broadcasted the same message to other clients updating their local state of the users.

## When to use Elmish.Bridge
There are many scenarios where it makes sense to use Elmish.Bridge:

* Chat-like applications with many connected users through many channels
* Syncing price data in real-time while viewing ticket prices
* Multiplayer games that need real-time update of game states
* Other applications of web sockets through an Elmish model

## Things to consider
The biggest distinction between using this and "raw" Saturn is that your web server becomes a stateful service. This introduces several differences for application design.

1. The server state has a lifespan equal to the that of the process under which the server instance is running. This means if the server application restarts then the server state will be reset.

2. The server state is *local to the server instance*. This means that if you run multiple web servers, they won't be sharing the same server state by default.

As of now there is no built-in persistence for the state, but you can implement this yourself using any number of persistance layers such as Redis Cache, Azure Tables or Blobs etc.

In addition Elmish.Bridge does not use standard HTTP verbs for communication, but rather websockets. Therefore, it is not a suitable technology for an open web server that can serve requests from other sources than Elmish.Bridge clients.

## Learn more about Elmish.Bridge
Head over to [Elmish.Bridge](https://github.com/Nhowka/Elmish.Bridge) to learn more.
Using F# on both client and server is at the core of the SAFE stack, as it simplifies the way we think about building web applications by using the same language, idioms and in many cases sharing our code and domain models.

However, building a client and a server app requires a fundamentally different way of thinking. On the server side we build [stateless APIs in Saturn](../components/component-saturn.md) that map HTTP requests to internal functionality, whereas on the frontend we use the Elmish model, implementing the [model-view-update pattern](../components/component-elmish.md): a stateful pattern that lets us think about the application state as it evolves while the application is running.

Even though we use the same language across platforms, applying these two different programming models forces us to switch our way of thinking back and forth when writing code for the client and for the server. This is where the [Elmish.Bridge](https://github.com/Nhowka/Elmish.Bridge) library comes into play: it brings the Elmish programming model *to the server* and unifies the way we write the application as a whole.

## How does Elmish work on the server?
Think of Elmish on the server as the model-view-update pattern but *without the view part*. Instead, you only need to implement `init` and `update` functions to manage the *server* state as it evolves while the server is running.

* Server state can contain data that is relevant to a single or all clients
* The dispatch loop running on the server is connected to the dispatch loop on the client via a *persistent stateful websocket connection*
* The `update` functions on client and server can exchange data via message passing.

## A simple example
Let's see a simple example of how this might work in practice:

```fsharp
// Client-side
let update msg state =
match msg with
| LoadUsers ->
// send the message to the server
state, Cmd.bridgeSend ServerMsg.LoadUsers
| UsersLoaded users ->
// receive message from the server
let nextState = { state with Users = users }
nextState, Cmd.none
// Server-side
let update clientDispatch msg state =
match msg with
| ServerMsg.LoadUsers ->
let loadUsersCmd =
Cmd.ofAsync
getUsersFromDb // unit -> Async<User list>
() // input arg = unit
UsersLoadedFromDb // User list -> ServerMsg
DoNothing // ServerMsg
state, loadUsersCmd
| ServerMsg.UsersLoadedFromDbSuccess users ->
// answer the current connected client with data
clientDispatch (ClientMsg.UsersLoaded users)
state, Cmd.none
| ServerMsg.DoNothing ->
state, Cmd.none
```

The above example mimics what would have been a `GET` request to the server to get user data from database. However, now the client sends a fire-and-forget message to the server to load users, and at some point the server messages the current client back with the results. Notice that the server could have decided to do other things than just messaging the client back: for example, it could have broadcasted the same message to other clients updating their local state of the users.

## When to use Elmish.Bridge
There are many scenarios where it makes sense to use Elmish.Bridge:

* Chat-like applications with many connected users through many channels
* Syncing price data in real-time while viewing ticket prices
* Multiplayer games that need real-time update of game states
* Other applications of web sockets through an Elmish model

## Things to consider
The biggest distinction between using this and "raw" Saturn is that your web server becomes a stateful service. This introduces several differences for application design.

1. The server state has a lifespan equal to the that of the process under which the server instance is running. This means if the server application restarts then the server state will be reset.

2. The server state is *local to the server instance*. This means that if you run multiple web servers, they won't be sharing the same server state by default.

As of now there is no built-in persistence for the state, but you can implement this yourself using any number of persistance layers such as Redis Cache, Azure Tables or Blobs etc.

In addition Elmish.Bridge does not use standard HTTP verbs for communication, but rather websockets. Therefore, it is not a suitable technology for an open web server that can serve requests from other sources than Elmish.Bridge clients.

## Learn more about Elmish.Bridge
Head over to [Elmish.Bridge](https://github.com/Nhowka/Elmish.Bridge) to learn more.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit 50826e6

Please sign in to comment.