Server Connection

In this example we use Core methods for connecting to the server using WebSockets.

We implement a button with counters like in Using widgets. The difference is that the state of the counters is kept in the simple Node.js (Express) server.

Here is the initial code for this example: ex15-server-connection-exercise

Dapplet with a connection to the server#

  1. We add the server's URL to ./config/default.json
1{
2 "dev": {
3 "serverUrl": "ws://localhost:8081/feature-1"
4 }
5}

It's possible to set different URLs for test and main networks.

In ./src/index.ts we get this URL from the config:

const serverUrl = await Core.storage.get('serverUrl')
  1. Then we have to create a connection with the server. Core.connect has its own state, so we have to provide the default state and its type or interface.
1interface IDappState {
2 amount: any
3}
4
5const defaultState: IDappState = { amount: 0 }
6const server = Core.connect<IDappState>({ url: serverUrl }, defaultState)

To understand how the dapplet's state works read the article Shared State.

In this simple example we can only use connection's state. But in your dapplet you might want to use one complex state for the entire app.

So let's create a common state.

const state = Core.state<IDappState>(defaultState)

Here we use the same interface and default state, but in your dapplet you can use other ones.

  1. In the config we get ctx. We can use ctx.id as a key in our states. If we use the common state, we can pass observable value of the server's state to it.
state[ctx.id].amount.next(server.state[ctx.id].amount)
  1. In the button's DEFAULT state we pass the observable counter to the label and in exec increase its value with a click. We implement the function that increases the counters on the server side. In the dapplet we call this function by using the send method. The first parameter is the name of the function and the second is a parameter for the server's function. In our case it's context ID.
1DEFAULT: {
2 img: EXAMPLE_IMG,
3 label: state[ctx.id].amount.value,
4 // label: server.state[ctx.id].amount, // alternative usage
5 exec: () => server.send('increment', ctx.id),
6},
caution

Note that we don't pass the entire observable state's amount, but instead it's value to the label. This is because this value is observable server's amount. When we directly use server.state[ctx.id].amount without the common state we don't have to get its value here.

This is the entire activate method:

1async activate() {
2 const serverUrl = await Core.storage.get('serverUrl');
3 const defaultState: IDappState = { amount: 0 };
4 const server = Core.connect<IDappState>({ url: serverUrl }, defaultState);
5 const state = Core.state<IDappState>(defaultState);
6
7 const { button } = this.adapter.exports;
8 this.adapter.attachConfig({
9 POST: (ctx: { id: string }) => {
10 state[ctx.id].amount.next(server.state[ctx.id].amount);
11 return button({
12 initial: 'DEFAULT',
13 DEFAULT: {
14 img: EXAMPLE_IMG,
15 label: state[ctx.id].amount.value,
16 // label: server.state[ctx.id].amount, // alternative usage
17 exec: () => server.send('increment', ctx.id),
18 },
19 });
20 },
21 });
22}

Server with the counters' storage#

  1. Add a storage for the counters in server/index.js.
const counter = {}
  1. Initialize a counter for the current tweet.
1if (!Object.prototype.hasOwnProperty.call(counter, tweetId)) {
2 counter[tweetId] = {
3 amount: 0,
4 }
5}
  1. Send the counter in params.
1ws.send(
2 JSON.stringify({
3 jsonrpc: '2.0',
4 method: subscriptionId,
5 params: [{ amount: counter[tweetId].amount }],
6 })
7)
  1. Send the counter in a callback.
1ws.send(
2 JSON.stringify({
3 jsonrpc: '2.0',
4 method: subscriptionId,
5 id: currentId,
6 params: [{ amount: counter[currentId].amount }],
7 })
8)
  1. Implement the counter increment.
const [currentId] = params
counter[currentId].amount += 1
emitter.emit('attached', currentId)

Here is the result code of the example: ex15-server-connection-solution

Run the dapplet:

npm i
npm start
caution

if an error occurs in the 'src/index.ts' , delete the 'package-lock.json' and run npm i

video