examples/fluence-js-examples/browser-example
renovate[bot] b537cdf345
Update dependency @fluencelabs/fluence to v0.27.5 (#413)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-12 09:12:12 -06:00
..
aqua Rename js-sdk-examples to fluence-js-examples 2021-10-20 22:03:31 +03:00
public Update Fluence JS to v0.23.0 (#267) 2022-04-27 15:23:57 +03:00
src Integration tests for all JS-related projects (#97) 2022-02-22 23:20:45 +03:00
.gitignore Update fluence-js version to 0.17.0 (AVM is running in the background) (#39) 2021-12-30 13:29:05 +03:00
.prettierrc.js Integration tests for all JS-related projects (#97) 2022-02-22 23:20:45 +03:00
jest.config.js Integration tests for all JS-related projects (#97) 2022-02-22 23:20:45 +03:00
package-lock.json Update dependency @fluencelabs/fluence to v0.27.5 (#413) 2023-01-12 09:12:12 -06:00
package.json Update dependency @fluencelabs/fluence to v0.27.5 (#413) 2023-01-12 09:12:12 -06:00
README.md Rename js-sdk-examples to fluence-js-examples 2021-10-20 22:03:31 +03:00
tsconfig.json Integration tests for all JS-related projects (#97) 2022-02-22 23:20:45 +03:00

Getting Started with Fluence

This sample project demonstrates how fluence network can be accessed from the browser. As an example it retrieves the timestamp of the current time from the relay node. The project is based on an create-react-app template with slight modifications to integrate Fluence. The primary focus is the integration itself, i.e React could be swapped with a framework of your choice.

Getting started

Run aqua compiler in watch mode:

npm run watch-aqua

Start the application

npm start

The browser window with localhost:3000 should open

How it works

The application can be split into two main building blocks: the runtime provided by the @fluencelabs/fluence package and the compiler for the Aqua language. The workflow is as follows:

  1. You write aqua code
  2. Aqua gets compiled into the typescript file
  3. The typescript is build by the webpack (or any other tool of you choice) into js bunlde.

Project structure

aqua                    (1)
 ┗ getting-started.aqua (3)
node_modules
public                  
src
 ┣ _aqua                (2)
 ┃ ┗ getting-started.ts (4)
 ┣ App.scss
 ┣ App.tsx
 ┣ index.css
 ┣ index.tsx
 ┣ logo.svg
 ┗ react-app-env.d.ts    
package-lock.json
package.json          
tsconfig.json

The project structure is based on the create-react-app template with some minor differences:

  • aqua (1) contains the Aqua source code files. The complier picks them up and generate corresponding typescript file. See getting-started.aqua (3) and getting-started.ts respectively
  • src/_aqua (2) is where the generated target files are places. The target directory is conveniently placed inside the sources directory which makes it easy to import typescript functions from the application source code

npm packages and scripts

The following npm packages are used:

  • @fluencelabs/fluence - is the client for Fluence Network running inside the browser. See https://github.com/fluencelabs/fluence-js for additional information
  • @fluencelabs/fluence-network-environment - is the maintained list of Fluence networks and nodes to connect to.
  • @fluencelabs/aqua - is the command line interface for Aqua compiler. See https://github.com/fluencelabs/aqua for more information
  • @fluencelabs/aqua-lib - Aqua language standard library
  • chokidar-cli - A tool to watch for aqua file changes and compile them on the fly

The compilation of aqua code is implemented with these scripts:

scripts: {
...
    "compile-aqua": "aqua -i ./aqua/ -o ./src/_aqua",
    "watch-aqua": "chokidar \"**/*.aqua\" -c \"npm run compile-aqua\""
}
...

The interface is pretty straightforward: you just specify the input and output directories for the compiler.

Aqua code

import "@fluencelabs/aqua-lib/builtin.aqua"

func getRelayTime(relayPeerId: PeerId) -> u64: (1)
    on relayPeerId:                            (2)
        ts <- Peer.timestamp_ms()              (3)
    <- ts                                      (4)

The code above defines a function which retrieves the current timestamp from the relay node. The function works as following:

  1. The function definition, specifying arguments and return value types
  2. Shift the execution to the peer with id equal to relayPeerId
  3. Calls built-in function on the current peer and stores the result into a variable
  4. Returns the result

The function gets compiled into typescript and can be called from the application code (see next section)

Application code

Let's take a look at how we can use Fluence from typecript.

First, we need to import the relevant packages:

import { createClient, FluenceClient } from "@fluencelabs/fluence";
import { krasnodar } from "@fluencelabs/fluence-network-environment";
import { getRelayTime } from "./_aqua/getting-started";

Please notice that the function defined in Aqua has been compiled into typescript and can be directly imported. Using the code generated by the compiler is as easy as calling a function. The compiler generates all the boilerplate needed to send a particle into the network and wraps it into a single call. Note that all the type information and therefore type checking and code completion facilities are there!

Next we initialize the client:

const relayNode = krasnodar[0];

function App() {
  const [client, setClient] = useState<FluenceClient | null>(null);

  ... 

  useEffect(() => {
    createClient(relayNode)
      .then((client) => setClient(client))
      .catch((err) => console.log("Client initialization failed", err));
  }, [client]);

Every peer running in the browser must connect to the network through a relay node. We use the first node of the krasnodar network there. In our example we store the client using React useState facilities. Feel free to store wherever you store other application state.

Executing Aqua is as easy as calling a function in typesctipt:

  const doGetRelayTime = async () => {
    if (!client) {
      return;
    }

    const time = await getRelayTime(client, relayNode.peerId);
    setRelayTime(new Date(time));
  };