Try the app here
React frontend repo here
This project is my own take on building Twitter clone, I have tried to keep things simple and concise. With minimal modules needed, it is very lightweight and fast, yet very functional and feature-rich. Three parts of project viz, front-end, database and api server are separately hosted and this repo contains the api code which connects to the front-end app and database.
-
Express as an api server
-
MongoDB as database
-
Mongoose as Mongo JavaScript driver, model/schema validation
-
Passport/passport-local for authentication
-
Bcryptjs for hashed password storage and comparison
-
Cookies/express-session/connect-mongo for session management and storage
-
And a large part of my otherwise useless brain.
File structure is now more standard and consice, here is a rundown of project structure
models/
trend.model.js
- contains mongoose schema's for models and their respective methods/statics
routes/
auth.js
- contains authentication related routes, like/auth/login
,/auth/logout
, etc.api.js
- contains all other routes
controllers/
user.controller.js
contains functions to be used in router, likegetUser
for/api/user/:username
endpoint- ...
serializers/
post.serializer.js
contains functions to serialize Post Object or Array and includes fields particular to authticated user.- ...
utils/
helpers.js
containes some miscellaneous helper utilities like escapeHtml, etcmddlewares.js
middlewares like ensureLoggedIn
dummy-data/
contains json and script for parsing some pre-populated datapassport.js
passport related congig and functionsapp.js
main express app.
Routes are divided into two parts /auth
(routes/auth.js) and /api
(routes/api.js). As it is inferred /auth contains authentication related sub routes, so user signup is done via POST /auth/signup
endpoint and login is done via POST /auth/login
. There is also GET /auth/login
which returns 200
if user is logged in (has a session up) and is used by front-end code to check for logged-in session. Other routes fall under /api
and these include GET /api/home_timeline
to get post feed of authenticated user, POST /api/post
to create a new post of user authenticated user, GET /api/search
for posts related query by the user (not needed to be authenticated) and GET /api/trends
to get trending hashtags,etc among others.
There is bunch of mongoose models viz Auth
, Post
, Friendship
, Trend
, home_timeline
, etc. All the functions which interacts with these models are contained in the same file as their schema (see models/). Any Api call or other models do not directly access the collection (represented by Models), instead they call their respective helper functions to get data, for example /api/home_timeline
calls getTimeline
in home_timline.js to get posts sorted and page wise. There is also some sandboxing to some models like Auth
and Friendship
, User
model do not even store the Reference (_id) of Auth
model or Friendship
model, instead each Auth
model containing user's hashed password, contains the user_id of user and not vice-versa. This accounts for additional security as User object is often populated into post object when sent to user, this method guaranties of no accidental leakage of password Hash or even its reference.
Some data (tweets and users) is fed into database at server-start to get a bunch of posts in tclone app (data is updated not overwritten). These are actual recent tweets on Twitter and fetched via twitter api and then populated in database. Tweet Model on this project is exactly compatible with Tweet objects returned by Twitter api and this data is read from dummy_data/home_timeline.json, which is original return value of twitter api /statuses/home_timeline. This file can also be filled with any list of valid tweet objects (models/post.js) and that data will be appended to database, all of this behavior is controlled by dummy_data/pre_populate.js which is invoked upon successful connection to the mongo database.
Each post added to database is parsed for any #hastags or @user_mentions. These hashtags (along with storing in Post.entities
) is stored in hashtags collection (Hashtag model) along with number of times it has been posted. From there Trending hashtags are retrieved as simply the ones with highest post volume . Selecting these highest volume hastags is done at a interval of (currently) 30 seconds, therefore trends are upadted realtime to what users are posting.
Posts can be searched for text they contain; user mentions they contain (@prefix in query) and hashtags they contain (#prefix in query). Text search is done via MongoDB text search of indexes and for user mentions and hashtags, these are simply compared from post.entities
.
Authentication is done with passport local-strategy with sessions managed server side via cookies which are also httpOnly. Along with an api end point for checking logged in session (GET /auth/login) which returns success if user is logged in, subsequent api requests return 403
to flag inauthentication and is also used by front end to destroy session cookie.
You will need some environment variables to run this, below is the how your environment variables should look like (.env
file on local and heroku variables on heroku and something similar elsewhere)
MONGO_URL=<link to atlas address or wherever your mongoDB is deployed, defaults to 'mongodb://localhost/test'>
SESSION_SECRET=<passed to session middleware, defaults to 'my shitty session secret'>
# Push notifications keys. You can generate them with command "./node_modules/.bin/web-push generate-vapid-keys"
PUBLIC_VAPID_KEY=<public vapid key which also goes into React front-end>
PRIVATE_VAPID_KEY=<corresponding private key>
# This must be either a URL or a 'mailto:' address.
# For example: 'https://my-site.com/contact' or 'mailto: contact@my-site.com'
WEB_PUSH_CONTACT="mailto: muzamilsofi@outlook.com"
After that just install deps and run npm run mon
to start dev server via nodemon and npm run start
to start server in production.
Are you serously still reading?