Web5: How to Query Records by Protocol Path

Rizèl Scarlett - Sep 30 '23 - - Dev Community

Starting a new job is thrilling because I get to learn new things. At my new job, I'm learning about Web5 and decentralization. So, I'm writing this blog post to share my insights about protocols and querying records.

When I built my first Web5 application in July, I explored how to store data in a Decentralized Web Node (DWN)—a personal data store in the Web5 ecosystem. I can write, edit, delete, and read data, also known as records, to my DWN.

To interact with data in my DWN, I need a Decentralized Identifier (DID). DIDs are unique IDs in the digital world. DIDs also act as keys to accessing and manipulating data in a DWN. I think of it like a password to access a database.

Although these were fairly new concepts, I drew parallels to help me understand. In my mind, I compared a DWN to a database and a DID to credentials that help users access a database.

However, there were some technical guidelines that initially confused me. For example – by default, I cannot write data to someone else's DWN unless I have been granted permission to do so. I wasn't sure how to grant permission for a user to write to my DWN. Similarly, I wasn't sure how another user could give me permission to write or read data on their DWN.

Apparently, the solution is to use a protocol! A protocol, structured as a JSON document, outlines the data structure, defines data types, and establishes rules for data access and interaction within a DWN. In other words, protocols enable users to control who can read or write specific types of data to your DWN.

Defining a protocol

I wrote a protocol definition for a chat application:

 const chatProtocolDefinition = {
      protocol: "https://blackgirlbytes.dev/ChatProtocol",
      published: true,
      types: {
        message: {
          schema: "https://schema.org/Message",
          dataFormats: ["application/json"],
        },
        audio: {
          schema: "https://schema.org/AudioObject",
          dataFormats: ["audio/mp3"],
        },
        video: {
          schema: "https://schema.org/VideoObject",
          dataFormats: ["video/mp4"],
        },
        image: {
          schema: "https://schema.org/ImageObject",
          dataFormats: ["image/png"],
        },
        gif: {
          schema: "https://schema.org/ImageObject",
          dataFormats: ["image/gif"],
        },
      },
      structure: {
        message: {
          $actions: [
            { who: "anyone", can: "write" },    
            { who: "author", of: "message", can: "read" },    
            { who: "recipient", of: "message", can: "read" },
          ],
          audio: {
            $actions: [
              { who: "anyone", can: "write" },    
              { who: "author", of: "video", can: "read" },    
              { who: "recipient", of: "video", can: "read" },
            ],
          },
          video: {
            $actions: [
              { who: "anyone", can: "write" },    
              { who: "author", of: "video", can: "read" },    
              { who: "recipient", of: "video", can: "read" },
            ],
          },
          image: {
            $actions: [
              { who: "anyone", can: "write" },    
              { who: "author", of: "video", can: "read" },    
              { who: "recipient", of: "video", can: "read" },
            ],
          },
          gif: {
            $actions: [
              { who: "anyone", can: "write" },    
              { who: "author", of: "video", can: "read" },    
              { who: "recipient", of: "video", can: "read" },
            ],
          },
        },
      },
    };
Enter fullscreen mode Exit fullscreen mode

This protocol definition supports various message types: text, audio, gif, video, or image. It also grants permissions to different groups of users. In this particular example, anyone can create a message, but the author and recipient also have permission to read messages. For clarity, this means anyone can write a message to the DWN, but only the recipients and senders can read the messages that they sent and received.

Sidenote: This method is an amazing way to intentionally architect applications.

Here's a brief explanation of some of the properties listed in the protocol definition:

  • types - Defines all the elements used in a protocol
  • structure - Outlines the relationship and interaction rules between different types
  • $actions - Specifies a set of permissions outlining who is allowed to perform specific actions like reading or writing on a given type

Installing a protocol

After I defined my protocol, I installed the protocol to my DWN. Installing the protocol enables my DWN to recognize and adhere to the rules and structures I've established. Here's how to install the protocol for the chat application:

const { protocolStatus } = await web5.dwn.protocols.configure({
  message: {
    definition: chatProtocolDefinition,
  },
});
Enter fullscreen mode Exit fullscreen mode

Problem: I want to filter for specific records

My protocol is defined and installed, so I can use it now! But there's a problem. I only want to return GIFs for a new feature in my chat application.

This new feature will display a gallery of users' most frequently used GIFs to help users conveniently retrieve the GIFs they want to use during conversation. I could fetch all the records on my DWN and then filter using an Array method on the cl. But, that could be inefficient.

Solution: Query by protocol path

One way I can address this is to query by protocolPath. I can pass the name of my structure as a value for my protocolPath property. In the protocol definition, gif is nested inside a message structure. Therefore, the protocol path for a gif is message/gif.

The query below will only fetch the GIF records:

const { records } = await web5.dwn.records.query({
  message: {
    filter: {
      protocol: "https://blackgirlbytes.dev/ChatProtocol",
      protocolPath: "message/gif",
    },
  },
});
Enter fullscreen mode Exit fullscreen mode

Other ways to query records

There are other ways to query records using the Web5 JS SDK. You can find a list of filterable record properties in the documentation. I’ve listed a couple of examples for your convenience:

Query Records by Record ID

The snippet below will return a record with the record ID: bfw35evr6e54c4cqa4c589h4cq3v7w4nc534c9w7h5

const { record } = await web5.dwn.records.query({
  message: {
    recordId: "bfw35evr6e54c4cqa4c589h4cq3v7w4nc534c9w7h5",
  },
});
Enter fullscreen mode Exit fullscreen mode

Query Records by Data Format

The snippet below will return all records that have an application/json data format.

const response = await web5.dwn.records.query({
  message: {
    filter: {
      dataFormat: 'application/json'
    }
  }
});
Enter fullscreen mode Exit fullscreen mode

Read the documentation

Remember that this technology is evolving rapidly! Web5 is in active development and technical preview. For the most current information and guidance on querying records in Web5, always refer to the official Query DWN Records guide.

Knowledge check

Excited to continue learning

Every Friday, the Developer Relations team has been spending time learning in public about Web5. Join us on Twitch.tv/tbdevs.

Check out a preview below:

You can watch the full episode here.

Like this content?

For more content like this, follow TBDevs and me!

. . . . . . . . . . . . . . . . . . . . . . . . . . . .