2021-08-02 05:46:14 +00:00
# Retrieve Messages Using Waku Store
2021-08-06 04:52:39 +00:00
DApps running on a phone or in a browser are often offline:
The browser could be closed or mobile app in the background.
2021-08-02 05:46:14 +00:00
[Waku Relay ](https://rfc.vac.dev/spec/18/ ) is a gossip protocol.
2021-08-05 03:08:01 +00:00
As a user, it means that your peers forward you messages they just received.
2021-08-02 05:46:14 +00:00
If you cannot be reached by your peers, then messages are not relayed;
2021-08-05 03:08:01 +00:00
relay peers do **not** save messages for later.
2021-08-06 04:52:39 +00:00
However, [Waku Store ](https://rfc.vac.dev/spec/13/ ) peers do save messages they relay,
allowing you to retrieve them at a later time.
The Waku Store protocol is best-effort and does not guarantee data availability.
Waku Relay should still be preferred when online;
Waku Store can be used after resuming connectivity:
2021-08-09 04:03:07 +00:00
For example, when the dApp starts.
2021-08-02 05:46:14 +00:00
2021-08-05 03:08:01 +00:00
In this guide, we'll review how you can use Waku Store to retrieve messages.
2021-08-06 04:52:39 +00:00
2021-08-05 03:08:01 +00:00
Before starting, you need to choose a _Content Topic_ for your dApp.
Check out the [how to choose a content topic guide ](choose-content-topic.md ) to learn more about content topics.
For this guide, we are using a single content topic: `/store-guide/1/news/proto` .
# Installation
You can install [js-waku ](https://npmjs.com/package/js-waku ) using your favorite package manager:
```shell
npm install js-waku
```
# Create Waku Instance
In order to interact with the Waku network, you first need a Waku instance:
```js
import { Waku } from 'js-waku';
2021-08-10 01:32:14 +00:00
const wakuNode = await Waku.create({ bootstrap: true });
2021-08-05 03:08:01 +00:00
```
2021-08-13 06:02:54 +00:00
Passing the `bootstrap` option will connect your node to predefined Waku nodes.
2021-08-10 01:32:14 +00:00
If you want to bootstrap to your own nodes, you can pass an array of multiaddresses instead:
2021-08-05 03:08:01 +00:00
```js
2021-08-10 01:32:14 +00:00
import { Waku } from 'js-waku';
const wakuNode = await Waku.create({
bootstrap: [
'/dns4/node-01.ac-cn-hongkong-c.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAkvWiyFsgRhuJEb9JfjYxEkoHLgnUQmr1N5mKWnYjxYRVm',
'/dns4/node-01.do-ams3.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAmPLe7Mzm8TsYUubgCAW1aJoeFScxrLj8ppHFivPo97bUZ'
]
});
2021-08-05 03:08:01 +00:00
```
# Use Protobuf
2021-08-09 04:03:07 +00:00
Waku v2 protocols use [protobuf ](https://developers.google.com/protocol-buffers/ ) [by default ](https://rfc.vac.dev/spec/10/ ).
2021-08-05 03:08:01 +00:00
2021-08-09 04:03:07 +00:00
Let's review how you can use protobuf to send structured data.
First, define a data structure.
2021-08-05 03:08:01 +00:00
For this guide, we will use a simple news article that contains a date of publication, title and body:
```js
{
date: Date;
title: string;
body: string;
}
```
To encode and decode protobuf payloads, you can use the [protons ](https://www.npmjs.com/package/protons ) package.
## Install Protobuf Library
First, install protons:
```shell
npm install protons
```
## Protobuf Definition
Then specify the data structure:
```js
import protons from 'protons';
const proto = protons(`
message ArticleMessage {
uint64 date = 1;
string title = 2;
string body = 3;
}
`);
```
You can learn about protobuf message definitions here:
[Protocol Buffers Language Guide ](https://developers.google.com/protocol-buffers/docs/proto ).
## Decode Messages
To decode the messages retrieved from a Waku Store node,
you need to extract the protobuf payload and decode it using `protons` .
```js
const decodeWakuMessage = (wakuMessage) => {
// No need to attempt to decode a message if the payload is absent
if (!wakuMessage.payload) return;
const { date, title, body } = proto.SimpleChatMessage.decode(
wakuMessage.payload
);
// In protobuf, fields are optional so best to check
if (!date || !title || !body) return;
const publishDate = new Date();
publishDate.setTime(date);
return { publishDate, title, body };
};
```
## Retrieve messages
You now have all the building blocks to retrieve and decode messages for a store node.
Retrieve messages from a store node:
```js
const ContentTopic = '/store-guide/1/news/proto';
waku.store
.queryHistory([ContentTopic])
.catch((e) => {
// Be sure to catch any potential error
console.log('Failed to retrieve messages', e);
})
.then((retrievedMessages) => {
const articles = retrievedMessages
.map(decodeWakuMessage) // Decode messages
.filter(Boolean); // Filter out undefined values
console.log(`${articles.length} articles have been retrieved`);
});
```
Note that `WakuStore.queryHistory` select an available store node for you.
2021-08-05 06:34:50 +00:00
However, it can only select a connected node, which is why the bootstrapping is necessary.
It will throw an error if no store node is available.
2021-08-05 03:08:01 +00:00
## Wait to be connected
Depending on your dApp design, you may want to wait for a store node to be available first.
2021-08-05 06:34:50 +00:00
In this case, you can listen for the [PeerStore's change protocol event ](https://github.com/libp2p/js-libp2p/blob/master/doc/API.md#known-protocols-for-a-peer-change )
to know whether any of your connected peers is a store peer:
2021-08-05 03:08:01 +00:00
```js
2021-08-05 06:34:50 +00:00
import { StoreCodec } from 'js-waku';
2021-08-05 03:08:01 +00:00
// Or using a callback
waku.libp2p.peerStore.on('change:protocols', ({ peerId, protocols }) => {
if (protocols.includes(StoreCodec)) {
// A Store node is available!
}
});
```