Chatter provides collaboration features and capabilities to applications built on the Force.com platform. This article provides an overview of Chatter, best practice strategies for simulating multiple Chatter participants within a Developer Edition (DE) environment, and a discussion of the underlying data model. It will help you understand how to leverage the Chatter platform for both custom applications built on Force.com, as well as with the salesforce.com applications such as Sales and Marketing. Along the way, you'll also learn how to programatically query and insert feeds.

Getting Started

The best way to learn Chatter and get a quick hands-on introduction is through the [Chatter Basics] module in Trailhead. You can also follow this brief Getting Started.

Chatter provides collaboration features and capabilities to any application built on the Force.com platform. These collaboration features include real-time feed updates, user profiles, and feeds that allow users to see changes that are important to them. For a developer, this social platform opens up endless possibilities for customization.

Let's begin at the beginning with User Profiles. A Chatter User Profile allows a user to share important information about themselves, and allows other users to learn more about you, including your interests, your specialties, and more. To update your User Profile, click on the Profile tab within the Salesforce Chatter Application.

Setting up a User Profile

With our User Profile updated, the next important piece of Chatter we need to become familiar with is Feed Tracking. Feed Tracking provides real-time notification of changes to a particular object. System Administrators may customize Feed Tracking on an object and field basis via Setup | Customize | Chatter | Feed Tracking. In addition to standard objects, any custom objects you create automatically appear as 'trackable' objects.

Feed Tracking on a Custom Object

Once Feed Tracking is enabled for a particular object, users can choose to follow individual records. For example, let's say I am interested in a new Opportunity titled New Gravel Pit. By clicking on the follow icon, I have just subscribed to the Feed and will be notified of any changes to this record (on tracked fields), and posts added by other users. You can also choose to follow individuals users and groups, and receive status updates.

Following an Opportunity

All changes to a followed record will automatically add a new status message to the record's feed. Feed Posts are not limited to textual comments however, they may also include links and files. In addition, Users may update their own status messages to let colleagues know what they are working on.

Sharing your User Status

Other Chatter features

In addition to the basic collaboration features such as Profiles, Feeds and following users and records, Chatter provides several other advanced features. Chatter supports the concept of Groups to foster collaboration between a specific set of users. For example, if you're working on a project and want to share information only with your team members, you can create a Chatter group for your team. Chatter Groups can be Public or Private. As the name implies, anyone can create and join a Public Group. However, you have to ask the Group owner for permission to join a Private Group.

Chatter also provides recommendations to help you identify the people, groups, and records that closely relate to your job and interests. In addition, users can 'Like' a Chatter post, mention other users using the '@John Doe' notation, share files privately with selected users and view currently trending topics.

Setting up a User Profile

Chatter Email settings

Now that we have our profile set up and started following some records, it is a good time to look at Chatter's Email settings found under Setup | My Chatter Settings | Chatter Email Settings. I like to receive email notifications of any changes, so I have checked this functionality in my profile. If, over time, you find you are receiving too many notifications you can adjust which actions trigger the notifications to be sent.

My Chatter Email Settings

That's it. In a few short minutes you have created a user profile, configured Feed Tracking and Chatter Notification settings, create a Group, followed your first record, and updated your status.

In the next section, we are going to leverage a useful strategy for working with multiple users within your Developer Edition environment.

Working With Multiple Users

Let's face it, chatting with yourself isn't much fun. Like any conversation, Salesforce Chatter becomes exponentially more powerful the more people are collaborating. Thankfully, the free Developer Edition environment comes enabled with a number of additional licenses to help you write your apps with multiple users in mind. A good strategy when developing Chatter applications is to create an additional user which you can use to test and simulate feed posts and conversations across multiple users.

Go ahead and create a new user now through Setup | Manage Users | Users. You may want to give the user a profile other than Salesforce Administrator to assist in testing out functionality.

Sammy the Sales Manager

Once you have successfully created your dummy user, a best practice is to login as that user and grant login access to Administrators. Granting login access significantly speeds up development and testing time by allowing you to rapidly switch between users without the need to login and logout every time.

Grant Login Access

Now log back out of your dummy account and log back into your System Administrator account. Navigate to Setup | Manage Users | Users and you should see a Login link beside your dummy account. Go ahead and click the Login link.

Login Link is now available

You will now be logged in as your dummy user. Notice the You are currently logged in as username message at the top of your browser window. This is extremely helpful when you are switching between users to help you know which user you are currently logged in as. Let's go and add a quick status update as our new user. Click on your user's name, enter a witty message, and click share.

Updating Sammy's status

Before we logout again, let's make sure our dummy user follows your other user. Similar to Feed Tracking, which notifies you of changes to a particular record, following a person lets you receive status updates. If you are not already there, navigate to the Salesforce Chatter app and click on the People tab. To follow another person, simply click the Follow icon.

Following People

Lastly, let's add a comment to the New Gravel Pit Opportunity, and logout of our dummy account to see the effect of our comment. Switch to the Sales application, click on the Opportunities tab, search for New Gravel Pit, add a comment, and click Share. Our original user, who is following this opportunity, will immediately be notified of the new comment.

Adding a new comment to an Opportunity

With our DE environment setup, and multiple users created, it's time to look under the covers of Chatter. The next section introduces the Chatter data model.

Chatter Data Model

The Chatter Data Model is centered around the FeedItem object which represent a Chatter 'post'. FeedItems can be of several different types and may have comments and 'likes' associated with them. FeedItems can also potentially have a list of associated changes identified as 'trackable' for standard and custom objects. The rest of the article will use the term 'post' interchangeably with FeedItem since posts is a familiar social networking concept.

Chatter Domain Model

Note: The FeedItem object was introduced in the Spring '11 release of Force.com (version 21 of the SOAP API). Prior to the Spring '11 release, the body and title for a Feed Item was stored in a separate object - FeedPost. FeedPost has been deprecated as of Spring '11, and though any existing code that references it will continue to be supported for backwards compatibility, any new code should not reference that entity.

FeedItem

FeedItem is the fundamental entity for the Chatter data model and it stores information about a Chatter post such as the body, title, and content related data if the post is a file upload. Every Chatter post results in a new FeedItem record. You can find additional details on the FeedItem fields here. Of special note is the parentId field. The parentId field represents the user, group or record that a particular post is related to. Since this field can be related to one of many different object types, it is termed a 'polymorphic' field.

Though the FeedItem object can be queried directly, you need to specify an Id filter in any FeedItem SOQL query. You're therefore unlikely to query the FeedItem object directly. For most Chatter related queries such as 'get all the Chatter posts for a given user/record', you'll query one of the aggregator objects like NewsFeed, UserProfileFeed etc. We'll cover these aggregator objects in more detail later in the article. You will most likely only reference the FeedItem object in the context of adding posts or writing Chatter triggers (more on those topics later).

Feed Types

Before we discuss Feed Items further, it is important to understand Chatter Feed Types. The available types, added automatically for you depending on what action you are performing, determine which records are created in the Chatter Data Model. The Feed Types are as follows:

TextPost

A TextPost type is used to identify a post you make from a record (in other words, not as part of a User status update). If your are typing something into a Chatter textbox which contains the message "Write something...", then you are creating a post of type TextPost.

Creating a TextPost type

UserStatus

A UserStatus identifies the post as a status update. The FeedItem created shall be accessible via the UserProfileFeed. A user's Chatter status is stored in the 'CurrentStatus' field on the User object. If the textbox for your post contains the message "What are you working on?", then you are creating a post of type UserStatus.

LinkPost

A record of type LinkPost shall be created whenever the User clicks the 'Link' field within the UI. Like all other posts, LinkPost types are added to the FeedItem object, but must be retrieved via an aggregator object like NewsFeed (see the SOQL statement in the Samples section below).

ContentPost

A record of type ContentPost shall be created whenever the User clicks the 'File' field within the UI, and uploads an attachment. The content data, filename, description, file size, and type are stored as a FeedItem record. ContentPost type posts must be retrieved via an aggregator object like NewsFeed (see the SOQL statement in the Samples section below).

TrackedChange

The TrackedChange type is used whenever a field on a record, previously identified during Chatter Feed Tracking configuration, is updated. In addition to the FeedItem being created, a FeedTrackedChange record is also created, including both the old and new value of the field. You cannot directly insert a FeedItem of type TrackedChange. This type of Chatter post is created automatically by the system based on the Chatter Feed Tracking configuration. We will discuss FeedTrackedChanges in more detail below.

The following image depicts elements in the UI and which Feed Types are used as a result:

Feed Types and how they relate to the Chatter UI

Entity Feeds

Entity Feeds represent standard or custom objects. For each of the standard objects, and any custom objects configured for Feed Tracking, an Entity Feed will be created automatically for you to maintain posts related to that object . The Chatter Data Model uses the naming convention listed below for these Entity Feeds:

1. Standard Objects such as Accounts, Contacts, Opportunities: ObjectNameFeed. For example, OpportunityFeed.

2. Custom Objects: ObjectName__Feed. For example MyCustomObject__Feed.

Every time a post is made on a tracked object, say Account, a record is added to the respective Entity Feed with the parentId field representing the Id of that particular object. In our example, this would be an Account Id. Querying the AccountFeed would return details about all Chatter posts made to Account records (unless the change on the object is due to tracked change, but we will discuss this further below).

The following example returns the 20 most recent Chatter posts for a given Account, ordered by CreatedDate, and Id descending:

SELECT Body, Type, Title, CreatedBy.Name 
FROM AccountFeed WHERE ParentId = <Account Record Id> 
ORDER BY CreatedDate DESC, Id DESC LIMIT 20

NewsFeed

The NewsFeed forms a sort of aggregate of posts of all users, groups and objects a particular user is following. Querying the NewsFeed object for a particular user would therefore return the same list of Chatter updates that the user sees on the Home tab of the Salesforce application. A user can only query their own News Feed. The following query for example returns the last 20 items from your NewsFeed

SELECT Id, Type, Body, Title, CreatedBy.Name  
FROM NewsFeed 
ORDER BY CreatedDate DESC, Id DESC LIMIT 20

Querying NewsFeed directly, while possible, is not always the most effective way to retrieve a particular user's feed (unless, of course you want everything a User has been posting about, and everything they are following.). Typically, querying based on Feed Items such as UserProfileFeed, or a particular Entity Feed is sufficient.

Note also that a NewsFeed includes mentions from other users. A mention is the @ symbol followed by a person’s name, for example, @Bob Smith. You can mention people in Chatter posts and comments and that automatically causes the post/comment to appear in that person's Chatter News Feed.

UserProfileFeed

UserProfileFeed is similar to NewsFeed in the sense that it acts as a aggregator of information, but unlike NewsFeed, it is specific to a particular user. UserProfileFeed can be very useful for retrieving a snapshot of a user's activity. The information obtained from a query to this view, returns the same information you would receive when clicking on a person from the People tab within Chatter.

When querying the UserProfileFeed you must use the SOQL function WITH (introduced in Spring '10), and pass in the id

SELECT Id, Type, Body, Title, CreatedBy.Name
FROM UserProfileFeed WITH UserId = <User Id>
ORDER BY CreatedDate DESC, Id DESC LIMIT 10

Similar to NewsFeed, a UserProfileFeed includes any mentions for the respective user.

CollaborationGroupFeed

The CollaborationGroupFeed represents the Chatter feed for a specific Chatter Group (Public or Private). Querying the CollaborationGroupFeed object for a specific group would result in the same set of posts that are displayed on the Group home page in Chatter.

SELECT Id, Type, Body, CreatedBy.Name 
FROM CollaborationGroupFeed where parentId = <Group Id>
ORDER BY createdDate DESC, Id DESC LIMIT 20

FeedTrackedChanges

During the Feed Tracking configuration, certain fields can be specified to send Chatter updates. If one of these fields is updated on a record, an entry shall be added to FeedTrackedChanges containing both the old and new value of the field (Note: this entry is in addition to any audit history tracking already established on an Object) with a Feed type of TrackedChange.

The FeedTrackedChanges object is not directly accessible and must be accessed via an Entity Feed:

SELECT Id, (SELECT Id, FieldName, OldValue, NewValue 
		     FROM FeedTrackedChanges ) 
FROM AccountFeed ORDER BY CreatedDate DESC, Id DESC LIMIT 10

FeedComment

Any time a User adds a comment to another User's post, a record is created in FeedComment with a reference to FeedItemId. The FeedComment is not directly accessible and must be accessed via an Entity Feed:

SELECT Id, (SELECT Id, CommentBody, CreatedDate, CreatedById, 
				   CreatedBy.FirstName, CreatedBy.LastName
		     FROM FeedComments ORDER BY CreatedDate DESC LIMIT 10)  
FROM AccountFeed ORDER BY CreatedDate DESC, Id DESC LIMIT 10

FeedLike

Users can 'Like' a Chatter post (but not a comment) by clicking on the respective link under the post. This action adds a record to the FeedLike object. FeedLike is not directly accessible and must be accessed via its parent NewsFeed, UserProfileFeed, or entity feed. For example:

SELECT Id, (SELECT CreatedById, CreatedBy.FirstName, CreatedBy.LastName
        FROM FeedLikes ORDER BY CreatedDate DESC)  
FROM AccountFeed ORDER BY CreatedDate DESC, Id DESC LIMIT 10

EntitySubscription

EntitySubscription records all the users and records that a user is following in Chatter. In effect, EntitySubscription is a simple junction object between the subscriber (subscriberId) and the user/record they're following (parentId). For example, the following query returns all the users and records that a particular user is following.

SELECT id, parentid, subscriberid, parent.name
FROM EntitySubscription WHERE subscriberid = <User Id>

CollaborationGroup, CollaborationGroupMember and CollaborationGroupMemberRequest

The CollaborationGroup and CollaborationGroupMember objects contain data about all Public and Private Chatter groups and their members. The CollaborationGroupMemberRequest object represents a request by a user to join a Private Chatter group.

ContentDocumentLink

A key component of any collaboration tool is the ability to share content and Chatter has a powerful framework for sharing files securely with other users and groups. The ContentDocumentLink object represents the link between a Salesforce CRM Content document or Chatter file and where it's shared. A file can be shared with other users, Chatter groups, records, and Salesforce CRM Content workspaces.

For example, the following query retrieves the Title and Description of the latest version of a document loaded via Chatter:


SELECT ContentDocument.LatestPublishedVersion.Title, ContentDocument.LatestPublishedVersion.Description 
FROM ContentDocumentLink 
WHERE LinkedEntityid = '<entity-id>'

CollaborationInvitation

You can invite people from your company that don't have Salesforce licenses to use Chatter. Invited users can view profiles, post on their feed, and join groups but can't see your Salesforce data or records. You can also invite coworkers to join a public group even if they don't use Chatter yet. The CollaborationInvitation object represents such a Chatter invitation request.

Chatter Data Model Examples

To complete our discussion on the Chatter Data Model, here are a few Apex and SOQL examples that let you perform the most common Chatter activities:

Adding a Post to an Object

Your most useful code snippet. This is where the simple, yet powerful, Chatter Data Model really shines. By changing the ParentId to any object which is being tracked, standard or custom, you can programatically insert a new feed post.

FeedItem post = new FeedItem();
post.ParentId = <my-object-id>; //eg. Opportunity id, custom object id..
post.Body = 'hello chatter';
insert post;

Updating a User's Status Message

To post a User's status message, simply update the User.CurrentStatus field and save the User:

 User user = [select id, CurrentStatus from User where id = :UserInfo.getUserId()];
 user.CurrentStatus = 'my new status message';
 update user;

Unlike directly creating a FeedItem, setting a user's status via User.CurrentStatus also updates the User.CurrentStatusLastModified timestamp, which is used on the People tab, and is displayed on the User Profile page. In addition, the Feed Type created as a result of the updating User.CurrentStatus will automatically be set to UserStatus

Creating a comment

The following code snippet creates a new comment.

FeedComment fcomment = new FeedComment();
fcomment.FeedItemId = <feedItemId>;
fcomment.CommentBody = 'This is a comment to your post';
insert(fcomment);

The code snippet above is pretty straightforward. The only thing of note is how to determine the parent FeedItemId for the comment - i.e. which Chatter post would you like to comment on. You can typically query the FeedItemId from any one of the aggregator objects like NewsFeed, UserProfileFeed or Entity Feed. For example the following query retrieves the FeedItem Id from the most recent entry in a user's feed:

SELECT id FROM UserProfileFeed WITh UserId = <User Id> 
ORDER BY createdDate DESC LIMIT 1 

The Id returned from the above query can then be used to set the 'FeedItemId' for inserting a comment for that post.

Retrieve Chatter Data

This SOQL statement, although more complex than the other examples, may be used as a generic query statement. I have used OpportunityFeed as an example, but you can query any Entity Feed object by changing the outer FROM clause.

SELECT Id, Type, 
    CreatedById, CreatedBy.FirstName, CreatedBy.LastName,
    ParentId, Parent.Name, 
    Body, Title, 
    (SELECT Id, FieldName, OldValue, NewValue 
            FROM FeedTrackedChanges ORDER BY Id DESC), 
    (SELECT Id, CommentBody, CreatedDate,
            CreatedById, CreatedBy.FirstName, CreatedBy.LastName
            FROM FeedComments ORDER BY CreatedDate LIMIT 10),
    (SELECT CreatedBy.FirstName, CreatedBy.LastName
            FROM FeedLikes)
FROM OpportunityFeed
WHERE ParentId = '<opportunity-id>'
ORDER BY CreatedDate DESC, Id DESC
LIMIT 20

When querying from an Entity Feed, if you don’t supply the WHERE ParentId = <‘entity-id’>, then you’ll actually get all feed items for all opportunities that you have access to. This is not always what you intend; You usually want to query for a specific entity, thus the ParentId as part of the WHERE clause.

Also, without the ORDER BY CreatedDate DESC on the outer query, you’ll get feed items in random order, and it again won’t be what you probably wanted, which is most recent data first. Chatter actually presents comments on the page in forward time order, and feed items in reverse order. You probably didn’t even notice; other popular social networking site use this order too, and it is usually how you want to read the data in your feed. Of course, if you don't want this order, remove the ORDER BY and DESC in the outer query.

You might have also noticed the inclusion of the 'Id DESC' clause in this and other queries referenced in the article. Adding this sort order, while of no consequence to the query result, significantly improves the performance of Chatter feed queries. It is therefore a general best practice to include 'Id DESC' sort order in all feed queries.

Finally, the LIMIT clause is important. In fact, when querying from NewsFeed (and in certain conditions, some other objects like UserProfileFeed), it is required. You almost never want all of the feed data, just the most recent. Feeds, by there nature can go large quite quickly, and performance of your query will be a function of how much data you pull back. A best practice is to always use the LIMIT keyword.

Developing Chatter Triggers

You can initiate real-time business actions every time someone posts or comments in Chatter by developing Apex triggers. The triggers can be written on the FeedItem and FeedComment objects. For example, the following trigger checks for a specific keyword in posts made to the Opportunity entity feed :

trigger CheckChatterPostsOnOpportunity on FeedItem (before insert) {
    //Get the key prefix for the Opportunity object via a describe call.
    String oppKeyPrefix = Opportunity.sObjectType.getDescribe().getKeyPrefix();
    
    for (FeedItem f: trigger.new)
    {
        String parentId = f.parentId;
        //We compare the start of the 'parentID' field to the Opportunity key prefix to
        //restrict the trigger to act on posts made to the Opportunity object.
        if (parentId.startsWith(oppKeyPrefix) && 
            f.Body == 'any  keyword here')
        {            
            <Add business logic here>
        }
    }
}

Note the use of the parentId prefix in the trigger code. Since the trigger is on FeedItem and not on UserProfileFeed or a specific entity feed (like AccountFeed, OpportunityFeed etc.) you have to examine the prefix of the 'polymorphic' parentId field to determine the type of the parent object for the chatter post.

Summary

Chatter provides a powerful tool for enabling collaboration within your organization. By following specific records, standard or custom objects, users and groups, business users can quickly keep up to date on information relevant to them.

At its core, Chatter is a social platform which can be extended to build collaboration-ready applications on Force.com. With point and click configuration, users can extend their custom applications and objects to take advantage of the feeds, posts, comments and user status.

By understanding the Chatter Data Model, developers can further extend their applications to programatically add posts and comments, or even expose Chatter actions for integration with external systems.

References

About the Authors

Quinton Wall is a Developer Evangelist at Salesforce.com, and a published fantasy author. He is a regular speaker at cloud events, contributor to the developer blogs, and twittersphere. When he is not working with the Force.com platform, or writing books, you can find him on the web.

Sandeep Bhanot is a Developer Evangelist at Salesforce.com. When not working on the Force.com platform, he spends most of his time reading Quinton's fantasy books.

Special thanks to Jon Mountjoy, Rob Woollen, Umit Yalcinalp, Carter Thaxton, Caroline Roth, Lauren Pederson, and Mark Leonard for their contributions to the article.