mirror of
https://github.com/fluencelabs/gitbook-docs
synced 2025-04-25 07:52:14 +00:00
Fix running in nodejs page
This commit is contained in:
parent
1a922f0077
commit
aa2c613ccb
@ -1,143 +1,124 @@
|
||||
# Intro
|
||||
|
||||
In this section we will show you how JS SDK can be used to create a hello world application with Fluence stack.
|
||||
JS SDK makes it easy to run applications in NodeJS environment. You can take full advantage of the javascript ecosystem and at the save time expose service to the Fluence Network. That makes is an excellent choice for quick prototyping of applications for Fluence Stack.
|
||||
|
||||
## Aqua code
|
||||
# Calc app example
|
||||
|
||||
Let's start with the aqua code first:
|
||||
Lets implement a very simple app which simulates a desk calculator. The calculator has internal memory and implements the following set of operations:
|
||||
|
||||
- Add a number
|
||||
- Subtract a number
|
||||
- Multiply by a number
|
||||
- Divide by a number
|
||||
- Get the current memory state
|
||||
- Reset the memory state to 0.0
|
||||
|
||||
First, let's write the service definition in aqua:
|
||||
|
||||
```
|
||||
service HelloWorld("hello-world"):
|
||||
hello(str: string)
|
||||
|
||||
func sayHello():
|
||||
HelloWorld.hello("Hello, world!")
|
||||
-- service definition
|
||||
service Calc("calc"):
|
||||
add(n: f32)
|
||||
subtract(n: f32)
|
||||
multiply(n: f32)
|
||||
divide(n: f32)
|
||||
reset()
|
||||
getResult() -> f32
|
||||
```
|
||||
|
||||
This file has two definitions. The first one is a service named `HelloWorld`. A Service interfaces functions executable on a peer. We will register a handler for this interface in our typescript application. The second definition is the function `sayHello`. The only thing the function is doing is calling the `hello` method of `HelloWorld` service located on the current peer. We will shouw you how to call this function from the typescript application.
|
||||
|
||||
## Installing dependencies
|
||||
|
||||
Initialze an empty npm package:
|
||||
|
||||
```bash
|
||||
npm init
|
||||
```
|
||||
|
||||
We will need these two packages for the application runtime
|
||||
|
||||
```bash
|
||||
npm install @fluencelabs/fluence @fluencelabs/fluence-network-environment
|
||||
```
|
||||
|
||||
The first one is the SDK itself and the second is a maintained list of Fluence networks and nodes to connect to.
|
||||
|
||||
Aqua compiler cli has to be installed, but is not needed at runtime.
|
||||
|
||||
**Warning: the package requires java to be installed \(it will call "java -jar ... "\)**
|
||||
|
||||
```bash
|
||||
npm install --save-dev @fluencelabs/aqua-cli
|
||||
```
|
||||
|
||||
Also we might want to have aqua source files automatically recompiled on every save. We will take advantage of chokidar for that:
|
||||
|
||||
```bash
|
||||
npm install --save-dev @fluencelabs/chokidar-cli
|
||||
```
|
||||
|
||||
And last, but no least we will need TypeScript
|
||||
|
||||
```
|
||||
npm install --save-dev typescript
|
||||
npx tsc --init
|
||||
```
|
||||
|
||||
## Setting up aqua compiler
|
||||
|
||||
Let's put aqua described earlier into `aqua\hello-world.aqua` file. You probably want to keep the generated TypeScript in the same directory with other typescript files, usually `src`. Let's create the `src\_aqua` directory for that.
|
||||
|
||||
The overall project structure looks like this:
|
||||
|
||||
```text
|
||||
┣ aqua
|
||||
┃ ┗ hello-world.aqua
|
||||
┣ src
|
||||
┃ ┣ _aqua
|
||||
┃ ┃ ┗ hello-world.ts
|
||||
┃ ┗ index.ts
|
||||
┣ package-lock.json
|
||||
┣ package.json
|
||||
┗ tsconfig.json
|
||||
```
|
||||
|
||||
The Aqua compiler can be run with `npm`:
|
||||
|
||||
```bash
|
||||
npx aqua-cli -i ./aqua/ -o ./src/_aqua
|
||||
```
|
||||
|
||||
We recommend to store this logic inside a script in `packages.json` file:
|
||||
|
||||
```json
|
||||
{
|
||||
...
|
||||
"scripts": {
|
||||
...
|
||||
"compile-aqua": "aqua-cli -i ./aqua/ -o ./src/_aqua", // (1)
|
||||
"watch-aqua": "chokidar \"**/*.aqua\" -c \"npm run compile-aqua\"" // (2)
|
||||
},
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
`compile-aqua` (1) runs the compilation once, producing `src\_aqua\hello-world.ts` in our case
|
||||
`watch-aqua` (2) starts watching for any changes in .aqua files recompiling them on the fly
|
||||
|
||||
## Using the compiled code in our application
|
||||
|
||||
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. It also generate a function for service callback registration. Note that all the type information and therefore type checking and code completion facilities are there!
|
||||
|
||||
Let's see how use generated code in our application. `index.ts`:
|
||||
Now write the implementation for this service in typescript:
|
||||
|
||||
```typescript
|
||||
import { FluencePeer } from "@fluencelabs/fluence";
|
||||
import { registerHelloWorld, sayHello } from "./_aqua/hello-world"; // (1)
|
||||
import { krasnodar } from "@fluencelabs/fluence-network-environment";
|
||||
import { registerCalc, CalcDef, demoCalculation } from "./_aqua/calc";
|
||||
|
||||
class Calc implements CalcDef {
|
||||
private _state: number = 0;
|
||||
|
||||
add(n: number) {
|
||||
this._state += n;
|
||||
}
|
||||
|
||||
subtract(n: number) {
|
||||
this._state -= n;
|
||||
}
|
||||
|
||||
multiply(n: number) {
|
||||
this._state *= n;
|
||||
}
|
||||
|
||||
divide(n: number) {
|
||||
this._state /= n;
|
||||
}
|
||||
|
||||
reset() {
|
||||
this._state = 0;
|
||||
}
|
||||
|
||||
getResult() {
|
||||
return this._state;
|
||||
}
|
||||
}
|
||||
|
||||
const keypress = async () => {
|
||||
process.stdin.setRawMode(true);
|
||||
return new Promise<void>((resolve) =>
|
||||
process.stdin.once("data", () => {
|
||||
process.stdin.setRawMode(false);
|
||||
resolve();
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
async function main() {
|
||||
await FluencePeer.default.init(); // (2)
|
||||
|
||||
registerHelloWorld({
|
||||
// (3)
|
||||
hello: async (str) => {
|
||||
console.log(str);
|
||||
},
|
||||
await FluencePeer.default.init({
|
||||
connectTo: krasnodar[0],
|
||||
});
|
||||
|
||||
await sayHello(); // (4)
|
||||
registerCalc(new Calc());
|
||||
|
||||
await FluencePeer.default.uninit(); // (5)
|
||||
console.log("application started");
|
||||
console.log("peer id is: ", FluencePeer.default.connectionInfo.selfPeerId);
|
||||
console.log(
|
||||
"relay is: ",
|
||||
FluencePeer.default.connectionInfo.connectedRelays[0]
|
||||
);
|
||||
console.log("press any key to continue");
|
||||
await keypress();
|
||||
|
||||
await FluencePeer.default.uninit();
|
||||
}
|
||||
|
||||
main();
|
||||
```
|
||||
|
||||
(1) Aqua compiler provides functions which can be directly imported like any normal typescript function.
|
||||
As you can see all the service logic has been implemented in typescript. You have full power of npm at your disposal.
|
||||
|
||||
(2) `FluencePeer` has to be initialized before running any application in Fluence Network. A peer represents the identity in the network, so most of the time you will only need a single peer per application. JS SDK provides a default instance which is accesible via `default` propery of the class. `init` method accepts a parameters object which will be covered in the next section. By default the peer is not get connected to the network and will only be able to execute air on the local machine only. Please keep in mind that the init function is asyncrhounous
|
||||
|
||||
For every exported `service XXX` definition in aqua code, the compiler provides a `registerXXX` counterpart. These funtions provide a type-safe way of registering callback handlers for the services. The callbacks are executed when the appropriate service is called in aqua on the current peer. The handlers take form of the object where keys are the name of functions and the values are async functions used as the corresponding callbacks. For example in (3) we are registering handler for `hello` function which outputs it's parameter to the console
|
||||
|
||||
For every exported `func XXX` definition in aqua code, the compiler provides an async function which can be directly called from typescripyt. In (4) we are calling the `sayHello` function with no arguments. Note that every function is asyncrhonous.
|
||||
|
||||
(5) You should call `uninit` method of `FluencePeer` when it is no longer needed. As a rule of thumb all the peers should be uninitilized before destroying the application.
|
||||
|
||||
Let's try running the example:
|
||||
Now try running the application:
|
||||
|
||||
```bash
|
||||
node -r ts-node/register src/index.ts
|
||||
> node -r ts-node/register src/index.ts
|
||||
|
||||
application started
|
||||
peer id is: 12D3KooWLBkw4Tz8bRoSriy5WEpHyWfU11jEK3b5yCa7FBRDRWH3
|
||||
relay is: 12D3KooWSD5PToNiLQwKDXsu8JSysCwUt8BVUJEqCHcDe7P5h45e
|
||||
press any key to continue
|
||||
|
||||
```
|
||||
|
||||
If everything has been done correctly yuo should see `Hello, world!` in the console.
|
||||
And the service can be called from aqua. For example:
|
||||
|
||||
The next secion will cover in-depth and advanced usage JS SDK
|
||||
```bash
|
||||
const peer ?= "12D3KooWLBkw4Tz8bRoSriy5WEpHyWfU11jEK3b5yCa7FBRDRWH3"
|
||||
const relay ?= "12D3KooWSD5PToNiLQwKDXsu8JSysCwUt8BVUJEqCHcDe7P5h45e"
|
||||
|
||||
func demoCalculation() -> f32:
|
||||
on peer via relay
|
||||
Calc.add(10)
|
||||
Calc.multiply(5)
|
||||
Calc.subtract(8)
|
||||
Calc.divide(6)
|
||||
res <- Calc.getResult()
|
||||
<- res
|
||||
```
|
||||
|
Loading…
x
Reference in New Issue
Block a user