Skip to main content

Extend news feed dapp

With our core in place we can now add the ability to follow other users and read a feed of their posts. Adding this functionality is clear in both Solidity and Rell. Let's start by extending our Solidity contract.

We add a function to follow a user, which takes both the address of the user and the user we want to follow. Then, an event is emitted to store the relationship between the user and the user to follow. In the upcoming authentication section, we will show how to make this more secure using the authenticated user, but for now, we take both users as parameters.

Solidity
event FollowUser(address indexed user, address indexed userToFollow);

function followUser(address user, address userToFollow) public {
emit FollowUser(user, userToFollow);
}

In Rell, we add an entity follow which defines a many-to-many relationship between a user and a follower. Then we add a function that creates a new entry in our relationship table. Again, we can store all data on-chain in our dapp.

Rell
entity follow {
index user;
index follows: user;
key user, follows;
}

// Then we add a follow method to follow a user
operation follow_user(user_id: byte_array, user_id_to_follow: byte_array) {
val user = user @ { .id == user_id };
val to_follow = user @ { .id == user_id_to_follow };
create follow ( user = user, follows = to_follow );
}

Breaking it down, our operation follow_user first fetches the entities for our two users. Then, it creates a new entry for our follow entity, establishing the relationship between a user and the user they follow. We can also simplify this by doing our user queries inline:

Rell
operation follow_user(user_id: byte_array, user_id_to_follow: byte_array) {
create follow ( user = user @ { .id == user_id }, follows = user @ { .id == user_id_to_follow } );
}

Fetching posts from followers

Now that we have the model and operations in place to follow users, let's look at how we can retrieve a feed from our followers. In Solidity, we need to do this in several steps. First, we fetch the followed users by querying the event log for FollowUser events matching our user's address. After this, we do another query where we iterate over our followed users and fetch their posts.

Solidity
const contract = new web3.eth.Contract(contractABI, contractAddress);

async function getFollowedUsers(web3, contract, userAddress) {
const followEvents = await contract.getPastEvents('FollowUser', {
filter: { user: userAddress },
fromBlock: 0,
toBlock: 'latest'
});

return followEvents.map(event => event.returnValues.userToFollow);
}

async function getPostsOfFollowedUsers(web3, contract, followedUsers) {
let posts = [];

for (const user of followedUsers) {
const userPosts = await contract.getPastEvents('PostCreated', {
filter: { user: user },
fromBlock: 0,
toBlock: 'latest'
});

posts = posts.concat(userPosts.map(event => ({
user: event.returnValues.user,
text: event.returnValues.text,
timestamp: new web3.utils.BN(event.returnValues.timestamp).toNumber(),
})));
}

return posts;
}

const followedUsers = await getFollowedUsers(web3, contract, 'userAddress');
const posts = await getPostsOfFollowedUsers(web3, contract, followedUsers);

This is a cumbersome way to fetch relational data. There are solutions to offload this into a relational database for frontend querying, but this increases complexity and makes the solution more centralized. In Rell, this can be handled efficiently on-chain, thanks to its relational approach. This code snippet shows a query that fetches all posts from users that the specified user is following.

Rell
query get_posts_from_followers(user_id: byte_array) {
val posts = (user, follow, post) @* {
.id == user_id, // Join on user id
follow.user == user, // Join on who the user follows
post.author == follow.follows // Join on posts for who the user follows
} (
post.timestamp,
post.content,
user_id = user_id
);

return (
posts = posts
);
}

In this query, we use joins to fetch all data in one go. Since Chromia's blockchain is relational and based on PostgreSQL, this is a very efficient query. With this section, we start seeing all the benefits of using a relational blockchain on Chromia. Next, we'll look into authentication in both Solidity and Rell.