Appbase.io News

Database service for the #realtimeweb

How to create a twitter clone in 300 lines of AngularJS

At Appbase, we decided to take our Javascript API on a test drive and build a complex real-time app using it! We didn't have to look much further - we found the right challenge in Twitter. Twitter has a rich follower based data model, and heavily uses real-time data reactivity. Our goal in building a twitter clone was to closely mimic the rich functionality provided by Twitter and avoid building a yet another half-baked clone.

tl;dr

Without further ado, here is the live version of the app - twitter.appbase.io.
You can also fork it from here - http://github.com/appbaseio/twitterclone.
An annotated version of the source code is here.

The architecture

We wanted to build the app with all the rich functionalities that we have come to associate with twitter -
1. a real-time user feed from all the people the user follows,
2. follow/unfollow users reactively, and
3. ability to search through tweets,
while satisfying our main design goal to write the app without using any server code.

We also added two additional global features keeping in mind to drive interactivity in our app:
1. A global feed of tweets which anyone visiting the twitter app can see.
2. A suggestion box to follow existing users.

Ingredients

We picked Bootstrap and AngularJS as our choice for front-end frameworks to build our single page twitter app. Appbase also has a handy AngularJS binding called ng-appbase-ref.js, which helps with binding Appbase data variables into the Javascript view.

These are the dependencies in our codebase.

<script src="js/bootstrap.min.js"></script>  
<script src="js/angular.min.js" type="application/javascript" ></script>  
<script src="js/angular-route.min.js" type="application/javascript"></script>  
<script src="http://cdn.appbase.io/appbase.min.js"></script>  
<script src="http://cdn.appbase.io/ng-appbase-ref.js"></script>  

Data modeling

Since Appbase supports a graph layout, we can represent our data as follows:

Namespaces:

We organize the data into three namespaces:
1. user - This namespace contains the primary data of users. For instance, a new user is created into this namespace. The information about the user's tweets, followers, follows is stored here as well.
2. tweet - Since we want to search on all the tweets, we will use the tweet namespace and enable search on it. Whenever a user tweets, we link her tweets to the tweet namespace (Note: this is not denormalization, we are actually linking the tweets).
3. global - The global namespace stores the data necessary for our global features, i.e. suggesting users to follow and global feed.

User data endpoints:

  • user/{uid}/tweets/{tid}
  • user/{uid}/followers/{fsid}
  • user/{uid}/following/{fgid}

Tweet data endpoints:

  • tweet/{t_id}

Finally, we also link user and tweet information in the global namespace for our additional features.

Global data endpoints:

  • global/users/{u_id}
  • global/tweets/{t_id}

User Views

We divided the entire application into 4 views - Global feed, Personal feed, User profile, Search.

View 1: Global feed

Global feed - twitter.appbase.io

The global feed displays the realtime feed of user activity, and has an option to login. The login feature uses localstorage for managing user sessions.

How does the global feed work?

In the global feed view, we listen on the global/tweets endpoint for a new tweet using an edge_added event like this:

$appbaseRef('global/tweets').$bindEdges($scope,'tweets')

View 2: Personal feed

Personal feed - twitter.appbase.io

The user's personal feed is generated when she logs in for the first time. We show the suggestions on the users she can follow, and show a personal feed from the people she is following!

Here's how the personal feed works:

We fetch the personal feed for the user at read time. This is better than fetching it at write time since a user can only follow so many people, a user's followers can be much more.

var usrRef = Appbase.ref('user/'+userSession.getUser()+'/following')  
usrRef.on('edge_added',function(error, followUserRef) {  
    if(error) throw error
    $scope.arraysOfTweets.push($appbaseRef(followUserRef).$outVertex('tweets').$bindEdges($scope))
})

We listen to the edge_added event on the user/{logged_user}/following endpoint, which notifies us every time the logged in user follows another user. At that point, we use the $bindEdges on the just followed user's tweet endpoint followUserRef. Now everytime that user tweets, the {logged_user} should see the tweet in her feed.

View 3: User Profile

Profile view - twitter.appbase.io

Profile view for the {logged_user} shows his profile, along with the list of users she is following and the list of users that follow her. When the {logged_user} views the profile of another user, she can additionally follow them (or unfollow).

View 4: Search

Search - twitter.appbase.io

There is a searchbox atop every view that allows searches on tweets.

How does the search work?

Appbase provides out of the box indexing and search functionality using the Appbase.search(namespace, {options}) method. All you need to do is enable search on the namespaces you would like from the Appbase developer console.

Appbase.search('tweet',{text: $routeParams.text, properties:['msg']},function(error, array) {  
    $scope.tweets = array
    $scope.$apply()
})

Summarizing

We were able to build the twitter app with all the above features over a weekend using just about 300 lines of AngularJS code. Feel free to check out the live demo, or look at the 300 lines of AngularJS code, or fork it and create your own version using Appbase.

Author image
Founder at Appbase, read my musings on the db world.