Guide 3 : Using Nested Streams to Access Nested Data Assets from a Primary Data Stream
This guide walks you through how to use Data Streams configured to be “nested streams” and how client-side apps can use the Itheum SDK to interact with these nested streams.
Firstly, what are nested streams and who are they needed? As detailed in the Data Streams Guides, we see that Data NFTs provide "web3 licensed" access to Data Streams, i.e., Data NFTs are a license to use your data. In most cases, your Data Stream will be "dynamic," where it can evolve to have its data change over time. An example of a dynamic Data Stream is could be some on-chain analytics data you have sourced, maintained, and kept updated, similar to the MultiversX Bubbles Data NFT. The Data Marshal can "stream out" the complete Data Stream once ownership of the Data NFT has been proved. The Data Marshal is also responsible for protecting the primary Data Stream link and aims never to expose the original data location (it only streams out the content).
The "hide original URL location and stream" mechanism of the Data Marshal works well when those above-mentioned "dynamic" Data Streams are mostly also "self-contained" and "complete" and don't contain links within them that need to be "hidden" as they are part of the original outcome of the Data Stream. For example, image files, PDFs, SVG documents, etc are most likely "complete" documents. So, in this situation, the Data Marshal does not need to hide and protect any secondary links located within the original data.
But what if your use-case requires that you have nested links within the original Data Stream that you also want to "hide" and protect, and use the same "hide original URL location and stream" features of the Data Marshal?
This is where "nested streams" come into play, but to get your head around it - let's look at a real-world example of nested streams being built on Itheum.
Building a "Music Playlist" Data NFT
You could build a music playlist or music player experience using Data NFTs, nested streams, and the Itheum Data NFT SDK
. In this use case, your primary Data Stream has a list of songs hosted in some location and would want the Data Marshal to mask/hide that data to protect the source/origin of the individual files. You can think of your primary "base" version of the Data Stream as a sort of manifest or instruction file that can be used by the client app to unpack a complete user experience. Nested streams allow for this functionality; in the example of the music player, you can unpack the original Data Stream as usual, but if the origin Data Stream server has flagged it as the “nested stream,” the Data Marshal is smart enough to discover and hide the links within it that need to be protected.
You can then use the Itheum Data NFT SDK
to iterate and stream the nested links while preserving the origin location of all the nested data assets. In the context of the music player, you get the primary "base" version of the nested stream, which provides all the data needed to render the music interaction (song titles, album art, etc.), but then you use the Data NFT SDK
to play the actual music assets when the user clicks on play, next, back etc. We will explore the code needed to do this below.
🏁 🏎️ Ready to start? Here are a few assumptions and pre-conditions
You need @itheum/sdk-mx-data-nft v2.1.0 or above to follow this guide.
This guide assumes that you are integrating with Itheum via an application built on the MultiversX blockchain, you use TypeScript/JavaScript as your programming language, and that you use that use the @multiversx/sdk-core and @multiversx/sdk-dapp libraries to authenticate and sign and track on-chain transactions. However, these libraries are optional and you can use any MultiversX tooling to interact with the blockchain.
To open a Data NFT and view its data using the
Data NFT SDK
, you have implemented MultiversX Native Auth integration with Itheum as per this guide Guide 2 : Unlocking Data NFTs via MultiversX Native AuthYou have control of the origin Data Stream publishing system (see Step 1 setup section below), as you will need to set HTTP Response Headers on the Data Stream.
Target User Story:
The target user story builds on the above Building a "Music Playlist" Data NFT section.
As an integrator of Itheum web3 Data Brokerage infrastructure:
I'd like to build a Music Player app that supports all Data NFTs that follow a standard "music player nested stream" format.
This Music Player app empowers the musicians who minted their songs as a Data NFT with the means to truly own their original music data.
This Music Player app can also evolve into a "decentralized Spotify," where I build and maintain the "front-end" music player features (and possibly charge a subscription fee for "premium" features), and musicians around the world can own their music data and earn royalties directly from the trade of their songs on my app.
Let's begin...
Step 1: Configure Data Stream Origin "Backend" for Nested Streams
Your Data Stream could be hosted by any of the data hosting service options provided inData Streams Guides. If you are not requiring the behaviour of a "nested stream", you do not need to do anything extra above hosting your data and making it available as a public URL. But in the event you do need to configure your Data Stream as a nested stream, then your backend Data Stream server will need to send down a custom HTTP Response Header.
We will make the "Response Header" name configurable, but for now, as we are still in "beta" mode, the target header needs to be :
Cannot add custom HTTP Response Headers? No worries, we got you!
If you are using a static, decentralized data stream that's hosted on IPFS, IPNS, or even on a centralized location and you CANNOT add HTTP Response headers, you can still trigger the Nested Stream workflow for your data by adding admf-nestedstream=1
parameter to your Data Stream URL when minting.
Here are some example Data Stream URLs with this parameter:
https://gateway.lighthouse.storage/ipfs/QmSpzKpcayVeAoMP89HAP5EijbAdUsg85PdGA4k4jxYqdh?dmf-nestedstream=1
ipns://my-hash?dmf-nestedstream=1
ipfs://my-CID?dmf-nestedstream=1
When the Data Marshal sees this HTTP header (or query string parameter) during the transit traffic, it will respond as per the rules of a nested stream. If the header is not found, it defaults to the usual behavior of a general Data Stream.
Your nested stream should ideally follow a data format that is similar to the below sample JSON document that details the nested stream payload for the Music Player app use-case mentioned above.
Here is a full sample file for you to inspect JSON content and HTTP response header:
In the data format above, the most important items that need to be included are :
The general JSON file skeleton structure seen above needs to be maintained.
The
data
key must exist as a first-level key and should have a list of data item objects.Each data item in the
data
list MUST have aidx
,date
andfile
field.idx
can start with 0 or 1 depending on your choice. We prefer 1.date
should be in the format "2023-01-16T00:00:00Z"The
file
should be a public URL that follows the standard Data Stream URL Rules
When the Data Marshal sees the header as above, it will serve the contents of the file but it will filter out the file
links to individual assets, but we will be able to programmatically interact with the Data Marshal and fetch and stream these file
links by using the idx
index value.
Let’s now see how we can interact with the data marshal and get it to fetch and serve individual assets.
Step 2: Installation of Itheum Data NFT SDK
In your NodeJS/JavaScript/TypeScript app, install the SDK @itheum/sdk-mx-data-nft
by following the instructions here. If you are using NPM then it would be.
Step 3: Enable Native Auth Login via MultiversX SDK Core
Step 4: Obtain an Auth "Session Token"
Step 5: "View Data" via a Native Auth session and handle the response
Please note that in addition to the code described above where you call dataNftToOpen.viewDataViaMVXNativeAuth
, you may also want to use the stream: true
setting to ensure the JSON version of the "base" data streams out and does not "auto-download" in your browser. See the next step for a code example of using the stream
param.
Step 6: Working with the Nested Stream
If we followed all the steps above, we now have access to the base version of the nested stream. If we print out the response from the previous, you will notice that you get the raw Data Stream contents, but as the Data Marshal has detected it to be a “nested stream”, it has extracted out and "hidden" all the file
attributes from the data
list. (Hint: compare the payload below with the original JSON document above to see the changes)
You can set up the UI with this “base” version as needed. In our case study, we can set a Music Player UI and load images, titles, etc.
And when a user selects a song and clicks on “play,” we can now interactively call the Data Marshal and “fetch” the nested file
links.
For example, let’s assume the user wants to play Beats are best with idx
“1”. Similar to the previous step where we "View Data", we now need to now call viewDataWithNativeAuth
again, but we add the target idx
of the link we want to stream as a new nestedIdxToStream
param. Also, do not forget to use the stream: true
setting to ensure the data streams out.
And that's it... using the above interactive coding method, we can iterate and interact with the "base" version of the Data Stream. By doing so, we can build a fully functional Audio Player app.
Other Use Cases for Nested Streams
As you would have seen by now, nested streams enable unique flexibility to Data NFTs as they allow for some programmatic, interactive iteration of links placed within a "base" Data Stream file. There are many use cases for this that we can explore, for example:
Any use case that requires a "manifest" or "instruction" type "base" file that contains "URLs" to other assets:
Audio Player
Video Player
Slideshows
Live Streams etc
"Paginating" through large datasets
We'd love to see what you can build with nested streams...
Last updated