Re-organise the docs to have clear CTAs (#251)

* Split website in 4:

- build
- run node
- learn
- research

* only show pages of section in sidebar

* home button on each sidebar

* index.md for homepages

* Add video tutorials

* delete old getting-started

* rename to "Waku node"

* fix nwaku compose

* add to dict

* script revert research changes

* fix broken links

* move research index content

* move research folder under learn

* move research folder under learn: side bar and buttons

* remove pointless links
This commit is contained in:
fryorcraken
2025-10-03 15:54:25 +10:00
committed by GitHub
parent 1da7137dfc
commit 0cb2be1c35
91 changed files with 569 additions and 813 deletions
+190
View File
@@ -0,0 +1,190 @@
---
title: Bootstrap Nodes and Discover Peers
hide_table_of_contents: true
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
This guide provides detailed steps to bootstrap your your node using [Static Peers](/learn/concepts/static-peers) and discover peers in the Waku Network using [DNS Discovery](/learn/concepts/dns-discovery).
:::info
If you do not set up a bootstrap node or discovery mechanism, your node will not connect to any remote peer.
:::
:::tip
Until [node incentivisation](/learn/research#prevention-of-denial-of-service-dos-and-node-incentivisation) is in place, you should [operate extra nodes](/run-node) alongside the ones provided by the Waku Network. When running a node, we recommend using the [DNS Discovery and Static Peers](#configure-dns-discovery-and-static-peers) configuration to connect to both the Waku Network and your node.
:::
## Default bootstrap method
The `@waku/sdk` package provides a built-in bootstrapping method that uses [DNS Discovery](/learn/concepts/dns-discovery) to locate peers from the `waku v2.prod` `ENR` tree.
```js
import { createLightNode } from "@waku/sdk";
// Bootstrap node using the default bootstrap method
const node = await createLightNode({ defaultBootstrap: true });
```
## Configure static peers
To set [static peers](/learn/concepts/static-peers), a list of `multiaddr` to bootstrap the node should be passed to the `bootstrapPeers` parameter of the `createLightNode()` function:
```js
import { createLightNode } from "@waku/sdk";
// Bootstrap node using static peers
const node = await createLightNode({
bootstrapPeers: ["[PEER MULTIADDR]"],
});
```
For example, consider a node that connects to two static peers on the same local host (IP: `0.0.0.0`) using TCP ports `60002` and `60003` with WebSocket enabled:
```js
// Define the list of static peers to bootstrap
const peers = [
"/ip4/0.0.0.0/tcp/60002/ws/p2p/16Uiu2HAkzjwwgEAXfeGNMKFPSpc6vGBRqCdTLG5q3Gmk2v4pQw7H",
"/ip4/0.0.0.0/tcp/60003/ws/p2p/16Uiu2HAmFBA7LGtwY5WVVikdmXVo3cKLqkmvVtuDu63fe8safeQJ",
];
// Bootstrap node using the static peers
const node = await createLightNode({
bootstrapPeers: peers,
});
```
Alternatively, you can dial a particular node like this:
```js
// Define the list of static peers to bootstrap
const peers = [
"/ip4/0.0.0.0/tcp/60002/ws/p2p/16Uiu2HAkzjwwgEAXfeGNMKFPSpc6vGBRqCdTLG5q3Gmk2v4pQw7H",
"/ip4/0.0.0.0/tcp/60003/ws/p2p/16Uiu2HAmFBA7LGtwY5WVVikdmXVo3cKLqkmvVtuDu63fe8safeQJ",
];
const node = await createLightNode();
// In case nodes are using IP address and / or `ws` protocol - additional configuration is needed:
/*
const node = await createLightNode({
libp2p: {
filterMultiaddrs: false,
},
});
*/
const promises = peers.map((multiaddr) => node.dial(multiaddr));
await Promise.all(promises);
```
:::tip
For local development using a `nwaku` node, use a `ws` address instead of `wss`. Remember that this setup is functional only when your web server is running locally. You can check how to get multi address of your locally run node in [Find node address](/run-node/find-node-address).
:::
## Configure DNS discovery
To bootstrap a node using [DNS Discovery](/learn/concepts/dns-discovery), first install the `@waku/dns-discovery` package:
<Tabs groupId="package-manager">
<TabItem value="npm" label="NPM">
```shell
npm install @waku/dns-discovery
```
</TabItem>
<TabItem value="yarn" label="Yarn">
```shell
yarn add @waku/dns-discovery
```
</TabItem>
</Tabs>
Then, use the `wakuDnsDiscovery()` function to provide a list of URLs for DNS node list in the format `enrtree://<key>@<fqdn>`:
```js
import { createLightNode } from "@waku/sdk";
import { wakuDnsDiscovery } from "@waku/dns-discovery";
// Define DNS node list
const enrTree = "enrtree://[PUBLIC KEY]@[DOMAIN NAME]";
// Define node requirements
const NODE_REQUIREMENTS = {
store: 3,
lightPush: 3,
filter: 3,
};
// Bootstrap node using DNS Discovery
const node = await createLightNode({
libp2p: {
peerDiscovery: [wakuDnsDiscovery([enrTree], NODE_REQUIREMENTS)],
},
});
```
For example, consider a node that uses the `waku v2.prod` and `waku v2.test` `ENR` trees for `DNS Discovery`:
```js
import { enrTree } from "@waku/dns-discovery";
// Bootstrap node using DNS Discovery
const node = await createLightNode({
libp2p: {
peerDiscovery: [
wakuDnsDiscovery([enrTree["PROD"], enrTree["TEST"]], NODE_REQUIREMENTS),
],
},
});
```
## Configure DNS discovery and static peers
You can also bootstrap your node using [DNS Discovery](/learn/concepts/dns-discovery) and [Static Peers](/learn/concepts/static-peers) simultaneously:
```js
import { createLightNode } from "@waku/sdk";
import { bootstrap } from "@libp2p/bootstrap";
import { enrTree, wakuDnsDiscovery } from "@waku/dns-discovery";
// Define the list of static peers to bootstrap
const peers = [
"/ip4/0.0.0.0/tcp/60002/ws/p2p/16Uiu2HAkzjwwgEAXfeGNMKFPSpc6vGBRqCdTLG5q3Gmk2v4pQw7H",
"/ip4/0.0.0.0/tcp/60003/ws/p2p/16Uiu2HAmFBA7LGtwY5WVVikdmXVo3cKLqkmvVtuDu63fe8safeQJ",
];
// Define node requirements
const NODE_REQUIREMENTS = {
store: 3,
lightPush: 3,
filter: 3,
};
// Bootstrap node using DNS Discovery and static peers
const node = await createLightNode({
libp2p: {
bootstrapPeers: peers,
peerDiscovery: [wakuDnsDiscovery([enrTree["PROD"]], NODE_REQUIREMENTS)],
},
});
```
## Retrieving connected peers
You can retrieve the array of peers connected to a node using the `libp2p.getPeers()` function within the `@waku/sdk` package:
```js
import { createLightNode } from "@waku/sdk";
const node = await createLightNode({ defaultBootstrap: true });
await node.waitForPeers();
// Retrieve array of peers connected to the node
console.log(node.libp2p.getPeers());
```
+98
View File
@@ -0,0 +1,98 @@
---
title: Debug Your Waku DApp and WebSocket
hide_table_of_contents: true
displayed_sidebar: build
---
This guide provides detailed steps to enable and use debug logs to troubleshoot your Waku DApp, whether in a NodeJS or browser environment and check your WebSocket connections in [nwaku](/run-node/).
## Enabling debug logs
When resolving issues in your Waku DApp, debug logs can be helpful. The `@waku/sdk` and `libp2p` packages use the debug tool to handle and show logs that help you debug effectively.
### NodeJS environments
To enable debug logs for `@waku/sdk` on NodeJS, you must set the `DEBUG` environment variable. To only enable debug logs for `@waku/sdk`:
```shell
export DEBUG=waku*
```
To enable debug logs for both `@waku/sdk` and `libp2p`:
```shell
export DEBUG=waku*,libp2p*
```
To enable debug logs for all components:
```shell
export DEBUG=*
```
### Browser environments
To view debug logs in your browser's console, modify the local storage and add the `debug` key. Here are guides for various modern browsers:
- [Google Chrome](https://developer.chrome.com/docs/devtools/storage/localstorage/)
- [Firefox](https://firefox-source-docs.mozilla.org/devtools-user/storage_inspector/local_storage_session_storage/index.html)
- [JavaScript](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage)
| KEY | VALUE | DESCRIPTION |
| - | - | - |
| `debug` | `waku*` | Enables `@waku/sdk` debug logs |
| `debug` | `waku*,libp2p*` | Enables `@waku/sdk` and `libp2p` debug logs |
| `debug` | `*` | Enables all debug logs |
## Checking WebSocket setup
[Nwaku](/run-node/) provides native support for WebSocket (`ws`) and WebSocket Secure (`wss`) protocols. These are the only [transports](/learn/concepts/transports) supported for connecting to the Waku Network via browsers.
It's important to note that browsers impose certain limitations on WebSocket usage:
- **Secure Context Requirement**: Insecure subroutines are prohibited in secure contexts. On an `https://` webpage, only `wss` connections are permitted, while `ws` connections are not allowed. This restriction does not apply if the webpage is served locally, like on `localhost` or `127.0.0.1`.
- **Certificate Validation**: Certificate validation rules are consistent for `https` and `wss` connections. Certificates must not be expired, issued by a recognized Certificate Authority (CA), and match the domain name, among other criteria.
- **User Feedback on Errors**: Web browsers do not display errors related to subroutines to the user. If a WebSocket connection encounters an issue, users won't be alerted directly; you'll need to check the browser's console for error details.
If you encounter difficulties when connecting to a remote node using `wss`, follow these steps:
### Try Websocat for connection
Attempt to connect using [websocat](https://github.com/vi/websocat), a tool for WebSocket interactions. Test the WebSocket port using the command:
```shell
websocat -v wss://[WEBSOCKET HOST]:[WEBSOCKET PORT]
```
For example, consider a `nwaku` node with the multiaddr as `/dns4/nwakunode.com/tcp/1234/wss/p2p/16...`:
```shell
$ websocat -v wss://nwakunode.com:1234
# ...
[INFO websocat::ws_client_peer] Connected to ws
```
The connection works if the `[INFO websocat::ws_client_peer] Connected to ws` log entry appears. If not, [check that the certificate is valid](#check-certificate-validity)
### Check certificate validity
Verify the certificate's validity by passing the `-k` or `--insecure` flag to handle invalid certificates in `websocat`:
```shell
websocat -v -k wss://nwakunode.com:1234
```
If this works, the certificate's invalidity is the problem, and you should investigate the cause of the error if not, [check if the WebSocket port is accessible](#check-websocket-port-accessibility).
### Check WebSocket port accessibility
Use `telnet` or another networking tool to verify if the WebSocket port is open and accessible. For example, if the multiaddr is `/dns4/nwakunode.com/tcp/1234/wss/p2p/16...`, use the command:
```shell
$ telnet nwakunode.com 1234
Trying 123.123.123.123...
Connected to nwakunode.com.
# ...
```
If the connection succeeds, there might be an issue with `nwaku`. Consider seeking support on the [Waku Discord](https://discord.waku.org) or [raise an issue](https://github.com/waku-org/nwaku/issues/new). If the connection fails, ensure that the WebSocket port is open.
+56
View File
@@ -0,0 +1,56 @@
---
title: JavaScript SDK FAQ
hide_table_of_contents: true
sidebar_label: Frequently Asked Questions
displayed_sidebar: build
---
import { AccordionItem } from '@site/src/components/mdx'
<AccordionItem title="How do I install the @waku/sdk package in my project?">
You can add the JavaScript SDK to your project using NPM, Yarn, or a CDN. Check out the <a href="/build/javascript/#installation">installation guide</a> to get started.
</AccordionItem>
<AccordionItem title="Why should I use Protocol Buffers for my application's message structure when using Waku?">
Protocol Buffers ensure consistent formatting, interoperability, and backward compatibility for your application's messages, with a smaller payload size than JSON. Check out the <a href="/build/javascript/#message-structure">installation guide</a> and <a href="https://protobuf.dev/overview/">Protobuf documentation</a> to learn more.
</AccordionItem>
<AccordionItem title="What are the steps to retrieve historical messages on Waku?">
Check out the <a href="/build/javascript/store-retrieve-messages">Retrieve Messages Using Store Protocol</a> guide to learn how to retrieve and filter historical messages using the <a href="/learn/concepts/protocols#store">Store protocol</a>.
</AccordionItem>
<AccordionItem title="How can I prevent Store peers from storing my messages?">
When <a href="/build/javascript/light-send-receive#choose-a-content-topic">creating your message encoder</a>, you can configure the <strong>ephemeral</strong> option to prevent Store peers from keeping your messages on the Waku Network.
</AccordionItem>
<AccordionItem title="How can I encrypt, decrypt, and sign messages in my Waku application?">
You can encrypt and decrypt your messages using symmetric, ECIES, and noise encryption methods. Check out the <a href="/build/javascript/message-encryption">Encrypt, Decrypt, and Sign Your Messages</a> guide to get started.
</AccordionItem>
<AccordionItem title="How do I integrate Waku into a React application?">
Waku has a specialized SDK designed for building React applications. Check out the <a href="/build/javascript/use-waku-react">Build React DApps Using @waku/react</a> guide for instructions on installation and usage.
</AccordionItem>
<AccordionItem title="How can I bootstrap and discover peers in the Waku Network for browser nodes?">
The JavaScript SDK has a <a href="/build/javascript/configure-discovery#default-bootstrap-method">default bootstrap method</a> that can be configured with <a href="/learn/concepts/static-peers">Static Peers</a> and <a href="/learn/concepts/dns-discovery">DNS Discovery</a>. Check out the <a href="/build/javascript/configure-discovery">Bootstrap Nodes and Discover Peers</a> guide for setting up peer discovery for your node.
</AccordionItem>
<AccordionItem title="How can I integrate Waku into a NodeJS application?">
Though the JavaScript SDK isn't directly usable in NodeJS due to <a href="/build/javascript/run-waku-nodejs">certain limitations</a>, we recommend running <a href="/run-node/run-docker-compose">nwaku in a Docker container</a> and consuming its <a href="https://waku-org.github.io/waku-rest-api/">REST API</a> in a NodeJS application.
</AccordionItem>
<AccordionItem title="How can I debug my Waku DApp and check WebSocket connections?">
Check out the <a href="/build/javascript/debug-waku-dapp">Debug Your Waku DApp and WebSocket</a> guide to discover how to use debug logs to troubleshoot your Waku DApp and resolve connection issues with nwaku WebSockets.
</AccordionItem>
<AccordionItem title="How can I manage unexpected disconnections of my Filter subscription from Waku?">
We recommend regularly pinging peers to check for an active connection and reinitiating the subscription when it disconnects. Check out the <a href="/build/javascript/manage-filter">Manage Your Filter Subscriptions</a> guide for a detailed explanation and step-by-step instructions.
</AccordionItem>
<AccordionItem title="How can I send images and videos on the Waku Network?">
While it's possible to transmit media such as images as bytes on Waku, we recommend uploading your media to a CDN or a file system like <a href="https://ipfs.tech/">IPFS</a> and then sharing the corresponding URL via Waku.
</AccordionItem>
<AccordionItem title="How can I connect to my own node?">
To manually set your own node as a starting point use <a href="/build/javascript/configure-discovery#configure-static-peers">Configure static peers</a>.
</AccordionItem>
+118
View File
@@ -0,0 +1,118 @@
---
title: JavaScript Waku SDK
hide_table_of_contents: true
displayed_sidebar: build
---
:::caution
Currently, the JavaScript Waku SDK (`@waku/sdk`) is **NOT compatible** with React Native. We plan to add support for React Native in the future.
:::
The [JavaScript Waku SDK](https://github.com/waku-org/js-waku) (`@waku/sdk`) provides a TypeScript implementation of the [Waku protocol](/) designed for web browser environments. Developers can seamlessly integrate Waku functionalities into web applications, enabling efficient communication and collaboration among users using the `@waku/sdk` package.
## Video Tutorials
<div class="video-container">
<iframe class="yt-video two-items" src="https://www.youtube.com/embed/PYQaXCxUCwA" title="Waku Tutorial 001: Introduction to Waku" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
<iframe class="yt-video two-items" src="https://www.youtube.com/embed/sfmMcrbiX0c" title="Build a game using Waku Protocol" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
</div>
## Installation
Install the `@waku/sdk` package using your preferred package manager:
```mdx-code-block
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
```
<Tabs groupId="package-manager">
<TabItem value="npm" label="NPM">
```shell
npm install @waku/sdk
```
</TabItem>
<TabItem value="yarn" label="Yarn">
```shell
yarn add @waku/sdk
```
</TabItem>
</Tabs>
You can also use the `@waku/sdk` package via a CDN without installing it on your system:
```js
import * as waku from "https://unpkg.com/@waku/sdk@latest/bundle/index.js";
```
## Message structure
We recommend creating a message structure for your application using [Protocol Buffers](https://protobuf.dev/) for the following reasons:
1. **Consistency:** Ensures uniform message format for easy parsing and processing.
2. **Interoperability:** Facilitates effective communication between different parts of your application.
3. **Compatibility:** Allows smooth communication between older and newer app versions.
4. **Payload Size:** Minimizes payload overhead, especially for byte arrays, unlike JSON which adds significant overhead.
To get started, install the `protobufjs` package using your preferred package manager:
<Tabs groupId="package-manager">
<TabItem value="npm" label="NPM">
```shell
npm install protobufjs
```
</TabItem>
<TabItem value="yarn" label="Yarn">
```shell
yarn add protobufjs
```
</TabItem>
</Tabs>
You can also use the `protobufjs` package via a CDN without installing it on your system:
```js
// Import the CDN
import "https://cdn.jsdelivr.net/npm/protobufjs@latest/dist/protobuf.min.js";
```
```html
<!-- Or include the protobufjs script -->
<script src="https://cdn.jsdelivr.net/npm/protobufjs@latest/dist/protobuf.min.js"></script>
```
## Getting started
Have a look at the quick start guide and comprehensive tutorials to learn how to build applications using `@waku/sdk`:
| Guide | Description |
|-----------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [Send and Receive Messages in a Reliable Channel](/build/javascript/reliable-channels) | Learn how to send and receive messages with a convenient SDK that provide various reliable functionalities out-of-the-box. |
| [Send and Receive Messages Using Light Push and Filter](/build/javascript/light-send-receive) | Learn how to send and receive messages on light nodes using the [Light Push](/learn/concepts/protocols#light-push) and [Filter](/learn/concepts/protocols#filter) protocols |
| [Retrieve Messages Using Store Protocol](/build/javascript/store-retrieve-messages) | Learn how to retrieve and filter historical messages on light nodes using the [Store protocol](/learn/concepts/protocols#store) |
| [Encrypt, Decrypt, and Sign Your Messages](/build/javascript/message-encryption) | Learn how to use the [@waku/message-encryption](https://www.npmjs.com/package/@waku/message-encryption) package to encrypt, decrypt, and sign your messages |
| [Build React DApps Using @waku/react](/build/javascript/use-waku-react) | Learn how to use the [@waku/react](https://www.npmjs.com/package/@waku/react) package seamlessly integrate `@waku/sdk` into a React application |
| [Scaffold DApps Using @waku/create-app](/build/javascript/use-waku-create-app) | Learn how to use the [@waku/create-app](https://www.npmjs.com/package/@waku/create-app) package to bootstrap your next `@waku/sdk` project from various example templates |
| [Bootstrap Nodes and Discover Peers](/build/javascript/configure-discovery) | Learn how to bootstrap your node using [Static Peers](/learn/concepts/static-peers) and discover peers using [DNS Discovery](/learn/concepts/dns-discovery) |
| [Run @waku/sdk in a NodeJS Application](/build/javascript/run-waku-nodejs) | Learn our suggested approach for using the `@waku/sdk` package within a NodeJS application |
| [Debug Your Waku DApp and WebSocket](/build/javascript/debug-waku-dapp) | Learn how to troubleshoot your Waku DApp using debug logs and check [WebSocket](/learn/concepts/transports) connections in [nwaku](/run-node/) |
| [Manage Your Filter Subscriptions](/build/javascript/manage-filter) | Learn how to manage [filter subscriptions](/learn/concepts/protocols#filter) and handle node disconnections in your application |
:::tip
Until [node incentivisation](/learn/research#prevention-of-denial-of-service-dos-and-node-incentivisation) is in place, you should [operate extra nodes](/run-node/) alongside the ones provided by the Waku Network. When running a node, we recommend using the [DNS Discovery and Static Peers](/build/javascript/configure-discovery#configure-dns-discovery-and-static-peers) configuration to connect to both the Waku Network and your node.
:::
## Get help and report issues
To engage in general discussions, seek assistance, or stay updated with the latest news, visit the `#support` and `#js-waku-contribute` channels on the [Waku Discord](https://discord.waku.org).
If you discover bugs or want to suggest new features, do not hesitate to [open an issue](https://github.com/waku-org/js-waku/issues/new/) in the [js-waku repository](https://github.com/waku-org/js-waku). Your feedback and contributions are highly valued and will help improve the `@waku/sdk` package.
+199
View File
@@ -0,0 +1,199 @@
---
title: Send and Receive Messages Using Light Push and Filter
hide_table_of_contents: true
displayed_sidebar: build
---
This guide provides detailed steps to start using the `@waku/sdk` package by setting up a [Light Node](/learn/glossary#light-node) to send messages using the [Light Push protocol](/learn/concepts/protocols#light-push), and receive messages using the [Filter protocol](/learn/concepts/protocols#filter). Have a look at the [installation guide](/build/javascript/#installation) for steps on adding `@waku/sdk` to your project.
## Create a light node
Use the `createLightNode()` function to create a [Light Node](/learn/glossary#light-node) and interact with the Waku Network:
```js
import { createLightNode } from "@waku/sdk";
// Create and start a Light Node
const node = await createLightNode({ defaultBootstrap: true });
await node.start();
// Use the stop() function to stop a running node
// await node.stop();
```
:::info
When the `defaultBootstrap` parameter is set to `true`, your node will be bootstrapped using the [default bootstrap method](/build/javascript/configure-discovery#default-bootstrap-method). Have a look at the [Bootstrap Nodes and Discover Peers](/build/javascript/configure-discovery) guide to learn more methods to bootstrap nodes.
:::
A node needs to know how to route messages. By default, it will use The Waku Network configuration (`{ clusterId: 1, shards: [0,1,2,3,4,5,6,7] }`). For most applications, it's recommended to use autosharding:
```js
// Create node with auto sharding (recommended)
const node = await createLightNode({
defaultBootstrap: true,
networkConfig: {
clusterId: 1,
contentTopics: ["/my-app/1/notifications/proto"],
},
});
```
### Alternative network configuration
If your project requires a specific network configuration, you can use static sharding:
```js
// Create node with static sharding
const node = await createLightNode({
defaultBootstrap: true,
networkConfig: {
clusterId: 1,
shards: [0, 1, 2, 3],
},
});
```
## Connect to remote peers
Use the `node.waitForPeers()` function to wait for the node to connect with peers on the Waku Network:
```js
// Wait for a successful peer connection
await node.waitForPeers();
```
The `protocols` parameter allows you to specify the [protocols](/learn/concepts/protocols) that the remote peers should have enabled:
```js
import { Protocols } from "@waku/sdk";
// Wait for peer connections with specific protocols
await node.waitForPeers([Protocols.LightPush, Protocols.Filter]);
```
## Choose a content topic
Choose a [content topic](/learn/concepts/content-topics) for your application and create a message `encoder` and `decoder`:
```js
import { createEncoder, createDecoder } from "@waku/sdk";
// Choose a content topic
const contentTopic = "/light-guide/1/message/proto";
// Create a message encoder and decoder
const encoder = createEncoder({ contentTopic });
const decoder = createDecoder(contentTopic);
```
The `ephemeral` parameter allows you to specify whether messages should **NOT** be stored by [Store peers](/build/javascript/store-retrieve-messages):
```js
const encoder = createEncoder({
contentTopic: contentTopic, // message content topic
ephemeral: true, // allows messages NOT be stored on the network
});
```
The `pubsubTopicShardInfo` parameter allows you to configure a different network configuration for your `encoder` and `decoder`:
```js
// Create the network config
const networkConfig = { clusterId: 3, shards: [1, 2] };
// Create encoder and decoder with custom network config
const encoder = createEncoder({
contentTopic: contentTopic,
pubsubTopicShardInfo: networkConfig,
});
const decoder = createDecoder(contentTopic, networkConfig);
```
:::info
In this example, users send and receive messages on a shared content topic. However, real applications may have users broadcasting messages while others listen or only have 1:1 exchanges. Waku supports all these use cases.
:::
## Create a message structure
Create your application's message structure using [Protobuf's valid message](https://github.com/protobufjs/protobuf.js#usage) fields:
```js
import protobuf from "protobufjs";
// Create a message structure using Protobuf
const DataPacket = new protobuf.Type("DataPacket")
.add(new protobuf.Field("timestamp", 1, "uint64"))
.add(new protobuf.Field("sender", 2, "string"))
.add(new protobuf.Field("message", 3, "string"));
```
:::info
Have a look at the [Protobuf installation](/build/javascript/#message-structure) guide for adding the `protobufjs` package to your project.
:::
## Send messages using light push
To send messages over the Waku Network using the `Light Push` protocol, create a new message object and use the `lightPush.send()` function:
```js
// Create a new message object
const protoMessage = DataPacket.create({
timestamp: Date.now(),
sender: "Alice",
message: "Hello, World!",
});
// Serialise the message using Protobuf
const serialisedMessage = DataPacket.encode(protoMessage).finish();
// Send the message using Light Push
await node.lightPush.send(encoder, {
payload: serialisedMessage,
});
```
## Receive messages using filter
To receive messages using the `Filter` protocol, create a callback function for message processing, then use the `filter.subscribe()` function to subscribe to a `content topic`:
```js
// Create the callback function
const callback = (wakuMessage) => {
// Check if there is a payload on the message
if (!wakuMessage.payload) return;
// Render the messageObj as desired in your application
const messageObj = DataPacket.decode(wakuMessage.payload);
console.log(messageObj);
};
// Create a Filter subscription
const { error, subscription } = await node.filter.createSubscription({ contentTopics: [contentTopic] });
if (error) {
// handle errors if happens
throw Error(error);
}
// Subscribe to content topics and process new messages
await subscription.subscribe([decoder], callback);
```
The `pubsubTopicShardInfo` parameter allows you to configure a different network configuration for your `Filter` subscription:
```js
// Create the network config
const networkConfig = { clusterId: 3, shards: [1, 2] };
// Create Filter subscription with custom network config
const subscription = await node.filter.createSubscription(networkConfig);
```
You can use the `subscription.unsubscribe()` function to stop receiving messages from a content topic:
```js
await subscription.unsubscribe([contentTopic]);
```
:::tip Congratulations!
You have successfully sent and received messages over the Waku Network using the `Light Push` and `Filter` protocols. Have a look at the [light-js](https://github.com/waku-org/js-waku-examples/tree/master/examples/light-js) and [light-chat](https://github.com/waku-org/js-waku-examples/tree/master/examples/light-chat) examples for working demos.
:::
+67
View File
@@ -0,0 +1,67 @@
---
title: Manage Your Filter Subscriptions
hide_table_of_contents: true
displayed_sidebar: build
---
This guide provides detailed steps to manage [Filter](/learn/concepts/protocols#filter) subscriptions and handle node disconnections in your application. Have a look at the [Send and Receive Messages Using Light Push and Filter](/build/javascript/light-send-receive) guide for using the `Light Push` and `Filter` protocols.
## Overview
Occasionally, your `Filter` subscriptions might disconnect from the Waku Network, resulting in messages not being received by your application. To manage your subscriptions, periodically ping peers to check for an active connection. The error message `"peer has no subscriptions"` indicates a failed ping due to disconnection. You can stop the pings if the disconnection/unsubscription is deliberate.
```mdx-code-block
import FilterPingFlow from "@site/diagrams/_filter-ping-flow.md";
<FilterPingFlow />
```
## Pinging filter subscriptions
The `@waku/sdk` package provides a `Filter.ping()` function to ping subscriptions and check for an active connection. To begin, create a `Filter` subscription:
```js
// Create a Filter subscription
const { error, subscription } = await node.filter.createSubscription({ contentTopics: [contentTopic] });
if (error) {
// handle errors if happens
throw Error(error);
}
// Subscribe to content topics and process new messages
await subscription.subscribe([decoder], callback);
```
Next, create a function to ping and reinitiate the subscription:
```js
const pingAndReinitiateSubscription = async () => {
try {
// Ping the subscription
await subscription.ping();
} catch (error) {
if (
// Check if the error message includes "peer has no subscriptions"
error instanceof Error &&
error.message.includes("peer has no subscriptions")
) {
// Reinitiate the subscription if the ping fails
await subscription.subscribe([decoder], callback);
} else {
throw error;
}
}
};
// Periodically ping the subscription
await pingAndReinitiateSubscription();
```
:::info
Pings will fail when there are temporary network degradations or reachability issues. This does not mean that the underlying connection has been closed.
:::
:::success Congratulations!
You have successfully managed your `Filter` subscriptions to handle node disconnections in your application.
:::
+236
View File
@@ -0,0 +1,236 @@
---
title: Encrypt, Decrypt, and Sign Your Messages
hide_table_of_contents: true
---
This guide provides detailed steps to use the [@waku/message-encryption](https://www.npmjs.com/package/@waku/message-encryption) package to encrypt, decrypt, and sign your messages using [Waku message payload encryption](/learn/glossary#waku-message-payload-encryption) methods.
:::info
Waku uses libp2p noise encryption for node-to-node connections. However, no default encryption method is applied to the data sent over the network. This design choice enhances Waku's encryption flexibility, encouraging developers to freely use custom protocols or [Waku message payload encryption](/learn/glossary#waku-message-payload-encryption) methods.
:::
## Installation
Install the required packages for integrating `@waku/message-encryption` using your preferred package manager:
```mdx-code-block
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
```
<Tabs groupId="package-manager">
<TabItem value="npm" label="NPM">
```shell
npm install @waku/message-encryption @waku/utils
```
</TabItem>
<TabItem value="yarn" label="Yarn">
```shell
yarn add @waku/message-encryption @waku/utils
```
</TabItem>
</Tabs>
## Symmetric encryption
`Symmetric` encryption uses a single, shared key for message encryption and decryption. Use the `generateSymmetricKey()` function to generate a random symmetric key:
```js
import { generateSymmetricKey } from "@waku/message-encryption";
// Generate a random symmetric key
const symmetricKey = generateSymmetricKey();
```
To send encrypted messages, create a `Symmetric` message `encoder` and send the message as usual:
```js title="Sender client"
import { createEncoder } from "@waku/message-encryption/symmetric";
// Create a symmetric message encoder
const encoder = createEncoder({
contentTopic: contentTopic, // message content topic
symKey: symmetricKey, // symmetric key for encrypting messages
});
// Send the message using Light Push
await node.lightPush.send(encoder, { payload });
```
To decrypt the messages you receive, create a symmetric message `decoder` and process the messages as usual:
```js title="Receiver client"
import { createDecoder } from "@waku/message-encryption/symmetric";
// Create a symmetric message decoder
const decoder = createDecoder(contentTopic, symmetricKey);
// Receive messages from a Filter subscription
await subscription.subscribe([decoder], callback);
// Retrieve messages from Store peers
await node.store.queryWithOrderedCallback([decoder], callback);
```
:::tip
The symmetric key exchange between users can happen through an [out-of-band method](/learn/glossary#out-of-band). For example, where the key is embedded within the URL shared by a user to access a specific resource.
:::
## ECIES encryption
`ECIES` encryption uses a public key for encryption and a private key for decryption. Use the `generatePrivateKey()` function to generate a random `ECDSA` private key:
```js
import { generatePrivateKey, getPublicKey } from "@waku/message-encryption";
// Generate a random ECDSA private key, keep secure
const privateKey = generatePrivateKey();
// Generate a public key from the private key, provide to the sender
const publicKey = getPublicKey(privateKey);
```
To send encrypted messages, create an `ECIES` message `encoder` with the public key and send the message as usual:
```js title="Sender client"
import { createEncoder } from "@waku/message-encryption/ecies";
// Create an ECIES message encoder
const encoder = createEncoder({
contentTopic: contentTopic, // message content topic
publicKey: publicKey, // ECIES public key for encrypting messages
});
// Send the message using Light Push
await node.lightPush.send(encoder, { payload });
```
To decrypt the messages you receive, create an `ECIES` message `decoder` with the private key and process the messages as usual:
```js title="Receiver client"
import { createDecoder } from "@waku/message-encryption/ecies";
// Create an ECIES message decoder
const decoder = createDecoder(contentTopic, privateKey);
// Receive messages from a Filter subscription
await subscription.subscribe([decoder], callback);
// Retrieve messages from Store peers
await node.store.queryWithOrderedCallback([decoder], callback);
```
:::tip
Users can share their public key through broadcasting or [out-of-band methods](/learn/glossary#out-of-band), such as embedding it in a URL or sending an unencrypted message on another content topic for others to retrieve.
:::
## Signing encrypted messages
Message signing helps in proving the authenticity of received messages. By attaching a signature to a message, you can verify its origin and integrity with absolute certainty.
:::info
Signing messages is only possible when encrypted, but if your application does not require encryption, you can generate a symmetric key through hardcoded or deterministic methods using information available to all users.
:::
The `sigPrivKey` parameter allows the `Symmetric` and `ECIES` message `encoders` to sign the message before encryption using an `ECDSA` private key:
```js title="Alice (sender) client"
import { generatePrivateKey, getPublicKey } from "@waku/message-encryption";
import { createEncoder as createSymmetricEncoder } from "@waku/message-encryption/symmetric";
import { createEncoder as createECIESEncoder } from "@waku/message-encryption/ecies";
// Generate a random ECDSA private key for signing messages
// ECIES encryption and message signing both use ECDSA keys
// For this example, we'll call the sender of the message Alice
const alicePrivateKey = generatePrivateKey();
const alicePublicKey = getPublicKey(alicePrivateKey);
// Create a symmetric encoder that signs messages
const symmetricEncoder = createSymmetricEncoder({
contentTopic: contentTopic, // message content topic
symKey: symmetricKey, // symmetric key for encrypting messages
sigPrivKey: alicePrivateKey, // private key for signing messages before encryption
});
// Create an ECIES encoder that signs messages
const ECIESEncoder = createECIESEncoder({
contentTopic: contentTopic, // message content topic
publicKey: publicKey, // ECIES public key for encrypting messages
sigPrivKey: alicePrivateKey, // private key for signing messages before encryption
});
// Send and receive your messages as usual with Light Push and Filter
await subscription.subscribe([symmetricEncoder], callback);
await node.lightPush.send(symmetricEncoder, { payload });
await subscription.subscribe([ECIESEncoder], callback);
await node.lightPush.send(ECIESEncoder, { payload });
```
You can extract the `signature` and its public key (`signaturePublicKey`) from the [DecodedMessage](https://js.waku.org/classes/_waku_message_encryption.DecodedMessage.html) and compare it with the expected public key or use the `verifySignature()` function to verify the message origin:
```js title="Bob (receiver) client"
import { generatePrivateKey } from "@waku/message-encryption";
import { createEncoder } from "@waku/message-encryption/symmetric";
// Generate a random private key for signing messages
// For this example, we'll call the receiver of the message Bob
const bobPrivateKey = generatePrivateKey();
// Create an encoder that signs messages
const encoder = createEncoder({
contentTopic: contentTopic,
symKey: symmetricKey,
sigPrivKey: bobPrivateKey,
});
// Modify the callback function to verify message signature
const callback = (wakuMessage) => {
// Extract the message signature and public key of the signature
// You can compare the signaturePublicKey with Alice public key
const signature = wakuMessage.signature;
const signaturePublicKey = wakuMessage.signaturePublicKey;
// Verify the message was actually signed and sent by Alice
// Alice's public key can be gotten from broadcasting or out-of-band methods
if (wakuMessage.verifySignature(alicePublicKey)) {
console.log("This message was signed by Alice");
} else {
console.log("This message was NOT signed by Alice");
}
};
await subscription.subscribe([encoder], callback);
```
## Storing encryption keys
We used randomly generated keys for encryption and message signing in the provided examples, but real-world applications require consistent keys among client restarts. Have a look at the [Key Pair Handling](https://github.com/waku-org/js-waku-examples/tree/master/examples/eth-pm/src/key_pair_handling) example, which demonstrates the secure storage and retrieval of key information from local storage using [Subtle Crypto](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto).
If you need a simple way to store your keys in hexadecimal format across your application, you can use the [@waku/utils](https://www.npmjs.com/package/@waku/utils) package:
```js
import { bytesToHex, hexToBytes } from "@waku/utils/bytes";
// Generate random symmetric and private keys
const symmetricKey = generateSymmetricKey();
const privateKey = generatePrivateKey();
// Store the keys in hexadecimal format
const symmetricKeyHex = bytesToHex(symmetricKey);
const privateKeyHex = bytesToHex(privateKey);
// Restore the keys from hexadecimal format
const restoredSymmetricKey = hexToBytes(symmetricKeyHex);
const restoredPrivateKey = hexToBytes(privateKeyHex);
```
:::tip Congratulations!
You have successfully encrypted, decrypted, and signed your messages using `Symmetric` and `ECIES` encryption methods. Have a look at the [eth-pm](https://github.com/waku-org/js-waku-examples/tree/master/examples/eth-pm) example for a working demo.
:::
<!-- [flush-notes](https://github.com/waku-org/js-waku-examples/tree/master/examples/flush-notes) and -->
+198
View File
@@ -0,0 +1,198 @@
---
title: Send and Receive Messages in a Reliable Channel
hide_table_of_contents: true
displayed_sidebar: build
---
Learn how to send and receive messages with a convenient SDK that provide various reliable functionalities out-of-the-box.
:::warning
This is an experimental feature and has a number of [limitations](https://github.com/waku-org/js-waku/pull/2526).
:::
## Import Waku SDK
```shell
npm install @waku/sdk@latest
```
Or using a CDN, note this is an ESM package so `type="module"` is needed.
```html
<script type="module">
import {
createLightNode,
ReliableChannel
} from 'https://unpkg.com/@waku/sdk@latest/bundle/index.js';
// Your code here
</script>
```
## Create a Waku node
Use the `createLightNode()` function to create a [Light Node](/learn/glossary#light-node) and interact with the Waku Network:
```js
import { createLightNode } from "@waku/sdk";
// Create a Light Node
const node = await createLightNode({ defaultBootstrap: true });
```
:::info
When the `defaultBootstrap` parameter is set to `true`, your node will be bootstrapped using the [default bootstrap method](/build/javascript/configure-discovery#default-bootstrap-method). Have a look at the [Bootstrap Nodes and Discover Peers](/build/javascript/configure-discovery) guide to learn more methods to bootstrap nodes.
:::
## Create encoders and decoders
Choose a [content topic](/learn/concepts/content-topics) for your application and create a message `encoder` and `decoder`:
```js
import { createEncoder, createDecoder } from "@waku/sdk";
// Choose a content topic
const ct = "/my-app/1/messages/proto";
// Create a message encoder and decoder
const encoder = node.createEncoder({ contentTopic: ct });
const decoder = node.createDecoder({ contentTopic: ct });
```
You can also use [`@waku/message-encryption`](/build/javascript/message-encryption) to encrypt and decrypt messages using Waku libraries.
:::info
In this example, users send and receive messages on a shared content topic. However, real applications may have users broadcasting messages while others listen or only have 1:1 exchanges. Waku supports all these use cases.
:::
## Listen for connection status
The Waku node will emit `health` events to help you know whether the node is connected to the network.
This can be useful to give feedback to the user, or stop some action (e.g. sending messages) when offline:
```js
import { HealthStatus } from "@waku/sdk";
node.events.addEventListener("waku:health", (event) => {
const health = event.detail;
if (health === HealthStatus.SufficientlyHealthy) {
// Show to the user they are connected
} else if (status === HealthStatus.MinimallyHealthy) {
// Maybe put some clue to the user that while we are connected,
// there may be issues sending or receiving messages
} else {
// Show to the user they are disconnected from the network
}
});
```
## Create a reliable channel
You need to choose a channel name: it acts as an identifier to the conversation, participants will try to ensure they all have the same
messages within a given channel.
```js
const channelName = "channel-number-15"
```
Finally, each participant need to identify themselves for reliability purposes, so they can confirm _others_ have received their messages.
It is up to you how to generate an id. Every participant **must** have a different id.
```js
const senderId = generateRandomStringId();
```
You can now create a reliable channel:
```js
import { ReliableChannel } from "@waku/sdk";
const reliableChannel = await ReliableChannel.create(node, channelName, senderId, encoder, decoder)
```
The channel will automatically start the Waku node and fetch messages.
## Create a message structure
Create your application's message structure using [Protobufjs](https://github.com/protobufjs/protobuf.js#usage):
```js
import protobuf from "protobufjs";
// Create a message structure using Protobuf
const DataPacket = new protobuf.Type("DataPacket")
.add(new protobuf.Field("timestamp", 1, "uint64"))
.add(new protobuf.Field("sender", 2, "string"))
.add(new protobuf.Field("message", 3, "string"));
```
:::info
Have a look at the [Protobuf installation](/build/javascript/#message-structure) guide for adding the `protobufjs` package to your project.
:::
## Listen to incoming messages
The reliable channel will emit incoming messages. To process them, simply add a listener:
```js
reliableChannel.addEventListener("message-received", (event) => {
const wakuMessage = event.detail;
// decode your payload using the protobuf object previously created
const { timestamp, sender, message } = DataPacket.decode(wakuMessage.payload);
// ... process the message as you wish
})
```
## Send messages
To send messages in the reliable channel, encode the message in a payload.
```js
// Create a new message object
const protoMessage = DataPacket.create({
timestamp: Date.now(),
sender: "Alice",
message: "Hello, World!",
});
// Serialise the message using Protobuf
const serialisedMessage = DataPacket.encode(protoMessage).finish();
```
Then, send the message and setup listeners so you can know when the message:
- has been sent
- has been acknowledged by other participants in the channel
- has encountered an error
```js
// Send the message, and get the id to track events
const messageId = reliableChannel.send(payload);
reliableChannel.addEventListener("sending-message-irrecoverable-error", (event) => {
if (messageId === event.detail.messageId) {
console.error('Failed to send message:', event.detail.error);
// Show an error to the user
}
})
reliableChannel.addEventListener("message-sent", (event) => {
if (messageId === event.detail) {
// Message sent, show '✔' to the user, etc
}
})
reliableChannel.addEventListener("message-acknowledged", (event) => {
if (messageId === event.detail) {
// Message acknowledged by other participants, show '✔✔' to the user, etc
}
})
```
:::tip Congratulations!
You have successfully sent and received messages over the Waku Network using our reliable protocols such as Scalable Data Sync (SDS) and P2P Reliability.
:::
+35
View File
@@ -0,0 +1,35 @@
---
title: "Run @waku/sdk in a NodeJS Application"
hide_table_of_contents: true
displayed_sidebar: build
---
While the `@waku/sdk` package is primarily designed for browser environments, you can use it in a NodeJS application. However, there are certain limitations and considerations to keep in mind. This guide provides a comprehensive overview of using `@waku/sdk` in NodeJS.
## Limitations
### API compatibility
`@waku/sdk` prioritises browser compatibility, avoiding NodeJS APIs for simpler bundling. This design choice enhances browser API compatibility but sacrifices NodeJS optimisation. While many browser APIs work in NodeJS, they might need better optimisation.
### Protocol implementation
`@waku/sdk` focuses on the client side of the [Request/Response protocol](/learn/concepts/network-domains#requestresponse-domain). We'll have to replicate all the functionalities added to [nwaku](/run-node/) to implement extra features.
### Codebase complexity
`@waku/sdk` aims to provide optimal default for the browser, striking a balance between browser and NodeJS compatibility while ensuring simplicity will add complexity.
### Browser-specific protocols
Certain features in `@waku/sdk` are tailored for browsers and might not translate seamlessly to NodeJS. For example, only `WebSocket` is supported in the browser, whereas a NodeJS application can benefit from using [transport methods](/learn/concepts/transports) like `TCP`.
`@waku/sdk` default peer management caters to the browser's ephemeral nature, which is different for NodeJS. This is why [DNS Discovery](/learn/concepts/dns-discovery) and [Peer Exchange](/learn/concepts/peer-exchange) are the default discovery mechanisms for the browser but not for NodeJS and desktop applications.
## Recommendations
Before using `@waku/sdk` in a NodeJS environment, take into account these limitations. For a more optimised solution, we recommend [running nwaku in a Docker container](/run-node/run-docker-compose) and consuming its [REST API](https://waku-org.github.io/waku-rest-api/).
## Future developments
There are plans to release a NodeJS package based on [nwaku](/run-node/) to streamline the process of using Waku Network features in NodeJS applications. You can track the progress and updates here: [https://github.com/waku-org/nwaku/issues/1332](https://github.com/waku-org/nwaku/issues/1332).
+206
View File
@@ -0,0 +1,206 @@
---
title: Retrieve Messages Using Store Protocol
hide_table_of_contents: true
displayed_sidebar: build
---
This guide provides detailed steps to create a Light Node for retrieving and filtering historical messages using the [Store protocol](/learn/concepts/protocols#store).
## Create a light node
Use the `createLightNode()` function to create a Light Node and interact with the Waku Network:
```js
import { createLightNode } from "@waku/sdk";
// Create and start a Light Node
const node = await createLightNode({ defaultBootstrap: true });
await node.start();
```
## Connect to store peers
Use the `node.waitForPeers()` method to wait for the node to connect with Store peers:
```js
import { Protocols } from "@waku/sdk";
// Wait for a successful peer connection
await node.waitForPeers([Protocols.Store]);
```
You can also specify a dedicated Store peer to use for queries when creating the node. This is particularly useful when running your own Store node or when you want to use a specific Store node in the network:
```js
const node = await createLightNode({
defaultBootstrap: true,
store: {
peer: "/ip4/1.2.3.4/tcp/1234/p2p/16Uiu2HAm..." // multiaddr or PeerId of your Store node
}
});
```
If the specified Store peer is not available, the node will fall back to using random Store peers in the network.
## Choose a content topic
[Choose a content topic](/learn/concepts/content-topics) for filtering the messages to retrieve and create a message `decoder`:
```js
import { createDecoder } from "@waku/sdk";
// Choose a content topic
const contentTopic = "/store-guide/1/message/proto";
// Create a message decoder
const decoder = createDecoder(contentTopic);
```
## Retrieve messages
`@waku/sdk` provides the `queryWithOrderedCallback()` and `queryGenerator()` functions for querying `Store` nodes and retrieving historical or missed messages. The responses from `Store` nodes are paginated and require you to process each page sequentially.
### `queryWithOrderedCallback`
The `store.queryWithOrderedCallback()` function provides a straightforward method for querying `Store` nodes and processing messages in chronological order through a callback function. It accepts these parameters:
- `decoders`: List of `decoders` that specify the `content topic` to query for and their [message decryption](https://rfc.vac.dev/waku/standards/application/26/payload) methods.
- `callback`: The callback function for processing the retrieved messages.
- `options` (optional): [Query options](/build/javascript/store-retrieve-messages#store-query-options) to filter the retrieved messages.
```js
// Create the callback function
const callback = (wakuMessage) => {
// Render the message/payload in your application
console.log(wakuMessage);
};
// Query the Store peer
await node.store.queryWithOrderedCallback([decoder], callback);
```
:::info
The `queryWithOrderedCallback()` function always returns the most recent messages in a page first.
:::
### `queryGenerator`
The `store.queryGenerator()` function provides more control and flexibility over processing messages retrieved from `Store` nodes through [Async Generators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncGenerator). It accepts these parameters:
- `decoders`: List of `decoders` that specify the `content topic` to query for and their [message decryption](https://rfc.vac.dev/waku/standards/application/26/payload) methods.
- `options` (optional): [Query options](/build/javascript/store-retrieve-messages#store-query-options) to filter the retrieved messages.
```js
// Create the store query
const storeQuery = node.store.queryGenerator([decoder]);
// Process the messages
for await (const messagesPromises of storeQuery) {
// Fulfil the messages promises
const messages = await Promise.all(
messagesPromises.map(async (p) => {
const msg = await p;
// Render the message/payload in your application
console.log(msg);
})
);
}
```
:::info
The `queryGenerator()` function always returns the oldest messages in a page first.
:::
## Store query options
### `pageDirection`
The `pageDirection` option specifies the direction in which pages are retrieved:
- `BACKWARD` (default): Most recent page first.
- `FORWARD`: Oldest page first.
```js
import { PageDirection } from "@waku/sdk";
// Retrieve recent messages first
const queryOptions = {
pageDirection: PageDirection.BACKWARD,
};
// Retrieve oldest messages first
const queryOptions = {
pageDirection: PageDirection.FORWARD,
};
// Query the Store peer with options
await node.store.queryWithOrderedCallback([decoder], callback, options);
const storeQuery = node.store.queryGenerator([decoder, options]);
```
### `cursor`
The `cursor` option specifies the starting index for retrieving messages. For example, consider a query that retrieves the first page messages and then continues with the next page:
```js
import { waku } from "@waku/sdk";
// Create the callback function
const messages = [];
const callback = (wakuMessage) => {
messages.push(wakuMessage);
// Return "true" to stop retrieving pages
// Here, it retrieves only the first page
return true;
};
// Retrieve the first page of messages
// This retrieves all the messages if "return true" is not present
await node.store.queryWithOrderedCallback([decoder], callback);
// Create the cursor
const lastMessage = messages[messages.length - 1];
const cursor = await waku.createCursor(lastMessage);
// Retrieve the next page of messages
// The message at the cursor index is excluded from the result
await node.store.queryWithOrderedCallback([decoder], callback, {
cursor: cursor,
});
console.log(messages);
```
:::info
If you omit the `cursor` option, the query will start from the beginning or end of the history, depending on the [page direction](#pagedirection).
:::
### `timeFilter`
The `timeFilter` option specifies a time frame to retrieve messages from. For example, consider a query that retrieves messages from the previous week:
```js
// Get the time frame
const endTime = new Date();
const startTime = new Date();
startTime.setDate(endTime.getDate() - 7);
// Retrieve a week of messages
const queryOptions = {
timeFilter: {
startTime,
endTime,
},
};
// Query the Store peer with options
await node.store.queryWithOrderedCallback([decoder], callback, options);
const storeQuery = node.store.queryGenerator([decoder, options]);
```
:::info
The `timeFilter` option significantly reduces message retrieval performance. To optimise it, consider resuming message retrieval using a [cursor](#cursor) that starts from the last seen message.
:::
:::tip Congratulations!
You have successfully retrieved and filtered historical messages on a Light Node using the `Store` protocol. Have a look at the [store-js](https://github.com/waku-org/js-waku-examples/tree/master/examples/store-js) and [store-reactjs-chat](https://github.com/waku-org/js-waku-examples/tree/master/examples/store-reactjs-chat) examples for working demos.
:::
+53
View File
@@ -0,0 +1,53 @@
---
title: "Scaffold DApps Using @waku/create-app"
hide_table_of_contents: true
displayed_sidebar: build
---
This guide provides detailed steps to bootstrap your next `@waku/sdk` project from [various example templates](https://github.com/waku-org/js-waku-examples/tree/master/examples) using the [@waku/create-app](https://www.npmjs.com/package/@waku/create-app) package.
## Usage
Initialise a new `@waku/sdk` template using any of the following methods:
```mdx-code-block
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
```
<Tabs>
<TabItem value="npm" label="NPM">
```shell
npx @waku/create-app [PROJECT DIRECTORY]
```
</TabItem>
<TabItem value="yarn" label="Yarn">
```shell
yarn create @waku/app [PROJECT DIRECTORY]
```
</TabItem>
</Tabs>
Next, select a template to initialise your app from:
![waku create app demo](/img/waku-create-app-demo.gif)
:::tip
If you have previously installed `@waku/create-app` globally, we recommend uninstalling the package to ensure that `npx` always uses the latest version.
:::
## Contributing new templates
We welcome and appreciate the contributions of templates for the `@waku/create-app` package. To contribute a template, please follow these steps:
1. Create the template, ensuring it is user-friendly and thoroughly tested.
2. Place the template in the `examples` folder in the [js-waku-examples](https://github.com/waku-org/js-waku-examples) repository's root.
3. Commit your changes with a detailed message and push them to your forked repository.
4. Finally, submit a pull request to the [js-waku-examples](https://github.com/waku-org/js-waku-examples) repository.
5. Our team will carefully review and merge your submission upon approval.
Waku also provides bounties to encourage community members to contribute to the network and earn rewards. To participate in the bounty program, head to [https://github.com/waku-org/bounties](https://github.com/waku-org/bounties).
+312
View File
@@ -0,0 +1,312 @@
---
title: "Build React DApps Using @waku/react"
hide_table_of_contents: true
displayed_sidebar: build
---
:::caution
Currently, the JavaScript Waku SDK (`@waku/sdk`) is **NOT compatible** with React Native. We plan to add support for React Native in the future.
:::
The [@waku/react](https://www.npmjs.com/package/@waku/react) package provides components and UI adapters to integrate `@waku/sdk` into React applications effortlessly. This guide provides detailed steps for using `@waku/react` in your project.
## Install the dependencies
First, set up a project using any [production-grade React framework](https://react.dev/learn/start-a-new-react-project) or an existing React application. For this guide, we will create a boilerplate using [ViteJS](https://vitejs.dev/guide/):
```mdx-code-block
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
```
<Tabs groupId="package-manager">
<TabItem value="npm" label="NPM">
```shell
npm create vite@latest [PROJECT DIRECTORY] -- --template react
```
</TabItem>
<TabItem value="yarn" label="Yarn">
```shell
yarn create vite [PROJECT DIRECTORY] --template react
```
</TabItem>
</Tabs>
Next, install the required packages for integrating `@waku/sdk` using your preferred package manager:
<Tabs groupId="package-manager">
<TabItem value="npm" label="NPM">
```shell
npm install @waku/react @waku/sdk protobufjs
```
</TabItem>
<TabItem value="yarn" label="Yarn">
```shell
yarn add @waku/react @waku/sdk protobufjs
```
</TabItem>
</Tabs>
## Initialise the Waku provider
In the `main.jsx` file, which serves as the entry point for a React app, we will set up the `LightNodeProvider` [context provider](https://react.dev/reference/react/createContext#provider) to wrap the entire application within the Waku provider. Import the following on top of your file:
```js title="src/main.jsx"
import { LightNodeProvider } from "@waku/react";
// Set the Light Node options
const NODE_OPTIONS = { defaultBootstrap: true };
ReactDOM.createRoot(document.getElementById('root')).render(
// Use the Light Node context provider
<React.StrictMode>
<LightNodeProvider options={NODE_OPTIONS}>
<App />
</LightNodeProvider>
</React.StrictMode>,
)
```
Next, create and start a [Light Node](/learn/glossary#light-node) using the `useWaku()` function within the `App.jsx` file:
```js title="src/App.jsx"
import { useWaku } from "@waku/react";
function App() {
// Create and start a Light Node
const { node, error, isLoading } = useWaku();
// "node" is the created Light Node
// "error" captures any error that occurs during node creation
// "isLoading" indicates whether the node is still being created
}
```
## Build the application interface
Let's build a user interface for sending messages and viewing past messages, modify the `App.jsx` file with the following code block:
```js title="src/App.jsx"
import { useState, useEffect } from 'react';
import { useWaku } from "@waku/react";
import { createEncoder, createDecoder } from "@waku/sdk";
import protobuf from 'protobufjs';
import './App.css'
function App() {
const [inputMessage, setInputMessage] = useState("");
const [messages, setMessages] = useState([]);
// Update the inputMessage state as the user input changes
const handleInputChange = (e) => {
setInputMessage(e.target.value);
};
// Create and start a Light Node
const { node, error, isLoading } = useWaku();
// Create a message encoder and decoder
const contentTopic = "/waku-react-guide/1/chat/proto";
const encoder = createEncoder({ contentTopic });
const decoder = createDecoder(contentTopic);
// Create a message structure using Protobuf
const DataPacket = new protobuf.Type("DataPacket")
.add(new protobuf.Field("timestamp", 1, "uint64"))
.add(new protobuf.Field("message", 2, "string"));
// Send the message using Light Push
const sendMessage = async () => {}
return (
<>
<div className="chat-interface">
<h1>Waku React Demo</h1>
<div className="chat-body">
{messages.map((message, index) => (
<div key={index} className="chat-message">
<span>{new Date(message.timestamp).toUTCString()}</span>
<div className="message-text">{message.message}</div>
</div>
))}
</div>
<div className="chat-footer">
<input
type="text"
id="message-input"
value={inputMessage}
onChange={handleInputChange}
placeholder="Type your message..."
/>
<button className="send-button" onClick={sendMessage}>Send</button>
</div>
</div>
</>
)
}
export default App
```
:::info
In the code above, we also created a message `encoder` and `decoder` using the `createEncoder()` and `createDecoder()` functions, along with the application [message structure](/build/javascript/#message-structure) with Protobuf.
:::
Next, modify the `App.css` file with the following code block:
```css title="src/App.css"
#root {
margin: 0 auto;
}
.chat-interface {
display: flex;
flex-direction: column;
height: 100vh;
border: 1px solid #ccc;
}
.chat-body {
flex-grow: 1;
overflow-y: auto;
padding: 10px;
}
.message-text {
background-color: #f1f1f1;
color: #000;
padding: 10px;
margin-bottom: 10px;
}
.chat-footer {
display: flex;
padding: 10px;
background-color: #f1f1f1;
align-items: center;
}
#message-input {
flex-grow: 1;
border-radius: 4px;
padding: 10px;
margin-right: 10px;
}
.send-button {
background-color: #007bff;
border-radius: 4px;
}
```
## Send messages using light push
To send messages in our application, we need to modify the `sendMessage()` function to serialize user input into our Protobuf structure and [push it to the network](/build/javascript/light-send-receive#send-messages-using-light-push) using the `useLightPush()` function:
```js title="src/App.jsx"
import { useLightPush } from "@waku/react";
function App() {
// Bind push method to a node and encoder
const { push } = useLightPush({ node, encoder });
// Send the message using Light Push
const sendMessage = async () => {
if (!push || inputMessage.length === 0) return;
// Create a new message object
const timestamp = Date.now();
const protoMessage = DataPacket.create({
timestamp: timestamp,
message: inputMessage
});
// Serialise the message and push to the network
const payload = DataPacket.encode(protoMessage).finish();
const { recipients, errors } = await push({ payload, timestamp });
// Check for errors
if (errors.length === 0) {
setInputMessage("");
console.log("MESSAGE PUSHED");
} else {
console.log(errors);
}
};
}
```
## Receive messages using filter
To display messages in our application, we need to use the `useFilterMessages()` function to create a [Filter subscription](/build/javascript/light-send-receive/#receive-messages-using-filter), receive incoming messages, and render them in our interface:
```js title="src/App.jsx"
import { useFilterMessages } from "@waku/react";
function App() {
// Receive messages from Filter subscription
const { messages: filterMessages } = useFilterMessages({ node, decoder });
// Render the list of messages
useEffect(() => {
setMessages(filterMessages.map((wakuMessage) => {
if (!wakuMessage.payload) return;
return DataPacket.decode(wakuMessage.payload);
}));
}, [filterMessages]);
}
```
## Retrieve messages using store
To display messages from the past, we need to retrieve them from the [Store protocol](/build/javascript/store-retrieve-messages) using the `useStoreMessages()` function when our application initialises and then render them alongside newly received messages:
```js title="src/App.jsx"
import { useFilterMessages, useStoreMessages } from "@waku/react";
function App() {
// Query Store peers for past messages
const { messages: storeMessages } = useStoreMessages({ node, decoder });
// Receive messages from Filter subscription
const { messages: filterMessages } = useFilterMessages({ node, decoder });
// Render both past and new messages
useEffect(() => {
const allMessages = storeMessages.concat(filterMessages);
setMessages(allMessages.map((wakuMessage) => {
if (!wakuMessage.payload) return;
return DataPacket.decode(wakuMessage.payload);
}));
}, [filterMessages, storeMessages]);
}
```
You can also configure a specific Store peer when creating the node, which is useful when running your own Store node or using a specific node in the network:
```js
const node = await createLightNode({
defaultBootstrap: true,
store: {
peer: "/ip4/1.2.3.4/tcp/1234/p2p/16Uiu2HAm..." // multiaddr or PeerId of your Store node
}
});
```
If the specified Store peer is not available, the node will fall back to using random Store peers in the network.
:::info
To explore the available Store query options, have a look at the [Retrieve Messages Using Store Protocol](/build/javascript/store-retrieve-messages#store-query-options) guide.
:::
:::tip
You have successfully integrated `@waku/sdk` into a React application using the `@waku/react` package. Have a look at the [web-chat](https://github.com/waku-org/js-waku-examples/tree/master/examples/web-chat) example for a working demo and the [Building a Tic-Tac-Toe Game with Waku](https://blog.waku.org/2024-01-22-tictactoe-tutorial/) tutorial to learn more.
:::