Retrieve Messages Using Store Protocol
This guide provides detailed steps to create a Light Node for retrieving and filtering historical messages using the Store protocol.
Create a light node
Use the createLightNode()
function to create a Light Node and interact with the Waku Network:
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 waitForRemotePeer()
function to wait for the node to connect with Store peers:
import { waitForRemotePeer, Protocols } from "@waku/sdk";
// Wait for a successful peer connection
await waitForRemotePeer(node, [Protocols.Store]);
Choose a content topic
Choose a content topic for filtering the messages to retrieve and create a message decoder
:
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 ofdecoders
that specify thecontent topic
to query for and their message decryption methods.callback
: The callback function for processing the retrieved messages.options
(optional): Query options to filter the retrieved messages.
// 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,
);
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. It accepts these parameters:
decoders
: List ofdecoders
that specify thecontent topic
to query for and their message decryption methods.options
(optional): Query options to filter the retrieved messages.
// 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);
})
);
}
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.
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:
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);
If you omit the cursor
option, the query will start from the beginning or end of the history, depending on the page direction.
timeFilter
The timeFilter
option specifies a time frame to retrieve messages from. For example, consider a query that retrieves messages from the previous week:
// 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]);
The timeFilter
option significantly reduces message retrieval performance. To optimise it, consider resuming message retrieval using a cursor that starts from the last seen message.
You have successfully retrieved and filtered historical messages on a Light Node using the Store
protocol. Have a look at the store-js and store-reactjs-chat examples for working demos.