Slack-inspired, single-page chat app with a RESTful Ruby on Rails API and PostgreSQL backend that serves JSON data to a React / Redux front-end.
Ruby on Rails
- back-end APIPostgreSQL
- databaseReact
- front-end renderingRedux
- front-end state managementjQuery
- AJAX requests and DOM manipulationPusher
- real-time chat and notificationsAWS S3
- cloud storage for user avatarsjbuilder gem
- data curation on back-endfigaro gem
- secure handling of API keyspaperclip gem
- used with ActiveRecord to allow models to store files easilybcrypt gem
- used for custom authentication logic
Lax has custom authentication logic developed from scratch using the BCrypt gem and hashing algorithm. User passwords are digested and stored in the database as hashes that are used by the SessionsController to manage login/logout logic. On successful login, each user is assigned a url-safe session token generated by the SecureRandom gem which is then stored in the current user's cookies to allow for current session management.
Live chat features are implemented using the Pusher API which allows for real-time, bidirectional communication via WebSockets. User actions on the front-end fire
AJAX requests that in turn trigger events on the back-end that are braodcast to all subscribers who are bound to those events. This gives users the ability to
experience updates without the need to refresh the page. The snippet below subscribes and binds the client to the new-message
event as soon as the component is
mounted, while the conditional statement ensures that only the current active channel sees the new message.
componentDidMount () {
const messages = this.pusher.subscribe('messages');
messages.bind('new-message', (data) => {
const message = data.message;
if (message.chatroomId === this.props.activeChannel.id) {
this.props.receiveMessage(message);
} else {
const notification = {
dmId: message.chatroomId,
authorId: message.userId,
authorUserName: message.author
};
this.props.receiveNotification(notification);
}
});
}
If a direct message arrives for a channel that is not the current active channel, a notification is displayed until the user visits the channel.
Users are able to create direct message channels with multiple participants, which allows for private, team chats that are not accessible by other users. In the direct message creation modal, users are able scroll and select chat participants by clicking or by searching via the input field. Because all registered users are fetched on component mount and stored as part of the front-end state, the search does not have to fire off multiple AJAX requests to query the database each time the search string changes. To provide users with a better experience and more information when choosing direct message participants, each users status is displayed and updated in real-time as users log in and log out.
To ensure good responsiveness, selective AJAX requests are used to fetch only the data for the current active channel and processing, where necessary, is done
mostly in the back-end to leverage the server's resources. In addition, eager loading through associations is used to avoid N+1 queries and to decrease the overhead
associated with connecting to the database multiple times. In the case of messages, which are a frequently fetched resource as users browse channels, the
includes
method is used to eagerly fetch each message's user
association so that the database is not hit multiple times when the data is required in the
jbuilder view.
def index
@messages = @chatroom.messages.includes(:user)
render 'api/messages/index'
end
Note: Implementing the above optimization decreased the ActiveRecord portion of fetching the largest channel by a factor of 10 on the production environment.
By using AWS S3 for storing avatar images uploaded by users, Lax is able to take advantage of Amazon’s CloudFront CDN service that accelerates web asset delivery through CDN caching, further adding to performance and responsiveness.
- Message Search
- Message Formatting
- GIF Support
- Emoticon Support