JAVASCRIPT

How to build your own Youtube – Part 2


Introduction: If you missed part 1, in this series we are covering how to build your own YouTube clone, Click here.

You need to have at least some prior experience with client-side Javascript frameworks and Node.js to get the most out of this tutorial.

Before proceeding, you need to make sure you have these tools installed:

  1. Node.js
  2. Bower
  3. MongoDB
  4. Nodemon

Step 1: Setting up new Express Project

Create a new directory called yourtube. Create two files package.json and server.js inside the folder.

screen shot 2016-06-08 at 12 52 15 pm

Open package.json and paste the following:

These are the node packages/modules that we will be using in this project. I’ll write a brief description for each package. In subsequent blog posts, as need arises, we’ll add more modules.

  1. async – For managing asynchronous flow
  2. bcrypt РFor hashing passwords
  3. body-parser – For parsing POST request data
  4. cloudinary – For uploading videos to Cloudinary Storage
  5. cors – For managing cross origin request permissions
  6. dotenv РFor reading environment values from the .env file
  7. express – Web framework for Node.js
  8. gravatar – For fetching a user’s avatar from the popular gravatar service
  9. jwt-simple – For creating json web tokens
  10. lodash РFor easy manipulation of data structure
  11. moment – For getting time stamps easily
  12. mongoose – MongoDB ODM with validation and Schema support
  13. morgan – HTTP Request logger

Run npm install  in the terminal to install the packages we specified above.

Open server.js and paste the following code . It’s a minimal¬†Express application enough to get started.

Next, quickly create a config folder in the yourtube directory. Create two files secrets.js and testdb.js inside the config folder. The server.js file is dependent on these two files.

secrets.js

This is where we extract all our secrets and environment variables.

testdb.js

This helps us in connecting to the MongoDB database. Now, make sure you have started your MongoDB server already by running mongod from your terminal.

Next, create a new folder called public in the yourtube directory. This is where we will place all our frontend/client code comprising of images, CSS and Javascript files.

Create a blank index.html file inside the public folder.

Run npm start in the Terminal to make sure our Express app is working without any issues.

You should see something like so:

screen shot 2016-06-08 at 1 33 36 pm

Step 2: Project Structure

The architecture of this app involves building our backend as an  API and consuming the API with a frontend framework. In this case, our frontend framework is AngularJS. We already created the public folder.

Go ahead and create css, img, js, and views folder inside the public directory.

screen shot 2016-06-08 at 1 46 42 pm

If you look at the diagram above, you’ll spot a vendor directory. This is where all our frontend libraries will be placed. How?

Next, create .bowerrc file in the yourtube directory and place this in it.

.bowerrc

With this, when we run bower install, it installs all the frontend libraries to the vendor directory.

Next, create a bower.json file in the yourtube root directory and place this in it.

bower.json

Run bower install to install all these libraries.

We have the understanding that the public directory will deal with the frontend code.

Next, create a folder in the root directory called server. Within this recently created folder, create controllers and models folder. Also, create a routes.js file within the server directory. This will handle our API routes.

One more thing, create a .env file within the root directory. This is where all our environment variables will be placed as we develop locally. Place this inside the .env file

.env

Our token secret and MongoDB URL. Quickly check the config/secrets.js, you’ll discover we used the node process function to read the environment variables after dotenv has loaded them into memory.

Step 3: Setting Up APIs

Let’s set up our server side Node APIs.

Right now, we need the following routes:

GET /api We really don’t need this route, but I think it’s not bad if we have it there .

GET & PUT /api/me, – This route is to allow us get the info of the logged-in user and also update his information.

POST/api/login, –¬†This route is to allow us authenticate a user’s details against the database to either allow the user login or not.

POST/api/register –¬†This route is to create a new user.

Create a user.server.model.js file in the server/models directory.

We’ll create our User Schema in this file. Mongoose allows us to create schemas that serve¬†as database representation of collections.

user.server.model.js

So our User collection will have the fullName, email, password, user_avatar and registered_on fields

The comparePassword method compares if the password supplied by the user matches that in the user collection.

The pre-save method hashes the password with bcrypt just before saving it in the user collection. Very awesome nifty feature of mongoose. :smile:

Next, Create a user.server.controller.js file in the server/controllers directory.

user.server.controller.js

Here we have five functions. Let’s break it down a little.

  1. The welcome function simply returns a json response that says “Welcome to Yourtube¬†Api”
  2. The registerUser function, with the aid of the User model checks if a user already exists with that email coming from the user input. If no user exists with that email, It gets the avatar of the user, saves the user details and creates a secure token else it returns an appropriate message. token.createJWT does the work of creating the token. The createJWT function can be found in the config/token file.
  3. The getLoggedInUserDetail function queries the user collection and returns the user detail.
  4. The updateLoggedInUserDetail function updates the user details in the user collection.
  5. The authenticate function verifies if the email and password matches before returning a valid token.

Note: I like to name my files using the .***.controller.js convention to help easily differentiate the client and server side controller files, same goes for services and models too.

Next, let’s create the routes and call the functions in this file.

Open routes.js and place this in it.

routes.js

Here we have the routes and the methods that will be called on each API route whenever a request is made to it.

Now, let’s test our routes. You can use POSTMAN or HTTPIE from your terminal

screen shot 2016-06-08 at 2 49 21 pm

screen shot 2016-06-08 at 3 06 15 pm

screen shot 2016-06-08 at 3 06 41 pm

You can try that with the register routes. Now if you notice, you’ll discover that anyone can actually access this API routes. That shouldn’t be the case, so let’s protect the routes with a middleware.

Step 4: Setting Up Middlewares

Open config/token.js and add this:

token.js

We have the createJWT method that sets the expiry date of the token to 14 days. Then we have the middleware in the form of ensureAuthenticated function.

Take a good look at this function.

It ensures there is an Authorization Header, if it doesn’t exist, it returns a 401 Unauthorized. If it exists, it grabs the value of the header, then decodes the token to obtain the user payload. If the token is valid, it allows the next function to be called else returns the appropriate message as seen above.

Next, let’s update our routes.js to have the middleware

routes.js

Now, make a request without any Authorization Header and token. We will have something like this:

screen shot 2016-06-08 at 3 33 51 pm

BRACE UP GUYS, LET’S GET TO THE FUN STUFF!!!

Step 5: Setting Up Frontend Login

Open public/index.html and add this:

index.html

With this, we have referenced all the relevant scripts. Angular, Satellizer( Used for Authentication), toastr, sanitize and the rest.

Note: There are better ways to add this. We can use Gulp and minify all of this into just one script. I actually recommend that for production apps. It helps to reduce the number of HTTP requests that our app serves.

Next ,create app.js and routes.js inside the public/js directory.

We will initialize all the modules inside app.js

app.js

Take a good look at the config method, we set some $authProvider properties.

This is the goodness of Satellizer.  Satellizer is a token-based AngularJS authentication library. Here, we set the login and signup remote url, Header, auth token and type of storage we want.

cfpLoadingBarProvider simply provides the Youtube top red loading bar effect.

Open public/js/routes.js and add this:

routes.js

This is where our client routes are defined. ngRoute is being used here.

The skipIfLoggedIn and loginRequired functions are very important here.

The skipIfLoggedIn function checks if the user has been authenticated, if he/she has been logged in, it rejects the promise.

Promises are alternatives to callbacks. You can either defer, reject or resolve a promise. Here we used $q РAngularJS inbuilt promise service. Learn more about Promises here.

The loginRequired function redirects the user back to the login page if he/she has not been logged in.

These two functions act as middlewares on our frontend. They prevent users from authorizing routes they don’t have privilege for.

I don’t like to see the hashbang in my AngularJS apps, So I turned it off with

Next, let’s create the login views and controller.

Before that, let’s create the home view, that’s the landing page

Create a pages folder inside the public/views directory. Then create a file home.client.view.html inside the folder. Open the file and add this:

home.client.view.html

Here we added about 6 youtube video URLs. Right now, they’ll be placeholders to make our homepage look beautiful. In the subsequent posts, we’ll remove them as we start uploading & hosting our videos on Yourtube using Cloudinary as the storage facility.

Before we open the page, let’s set up the NavbarCtrl.

Create a file nav.client.controller.js file inside public/js/controllers directory and add this:

nav.client.controller.js

This checks if a user is logged in or not. $auth service is invoked from Satellizer package.

Now, if you open the application, you should see this as the landing page.

screen shot 2016-06-08 at 11 12 12 am

Next, create an account folder inside public/views directory. Then, create a file login.client.view.html inside the folder. Open the file and add this:

login.client.view.html

Before, we view the login page. Let’s create the controller that handles the login page.

Create a controllers folder inside the public/js directory. Now, create auth.client.controller.js file inside the controllers folder.

Open the file and add this:

auth.client.controller.js

The $auth service from the Satellizer package has a login function that we simply called. It makes a request to our server side API login route. Now, this is our login page should look like:

screen shot 2016-06-08 at 7 23 25 am

When a user signs in successfully,

screen shot 2016-06-08 at 4 32 00 pm

When a user login details are not correct,

screen shot 2016-06-08 at 4 34 37 pm

Step 6: Setting up Frontend Signup

Create a create-user.client.view.html file inside the views/account directory.

Open the file and add this:

create-user.client.view.html

Next, Open the AuthController file auth.client.controller.js.¬†Let’s add the signup method like so:

auth.client.controller.js

Now, your signup should look like this:

screen shot 2016-06-08 at 7 23 34 am

A successful signup should look like this:

screen shot 2016-06-08 at 4 43 33 pm

Step 7: Setting Up Profile

Create a profile.client.controller.js file inside the public/js/controllers directory.

Open the file and add this:

profile.client.controller.js

Here we are getting the profile data and also have the function that updates the user profile. We injected the User service.

Create a services folder inside the public/js directory.

Create the User service user.client.service.js inside the services folder and add this:

user.client.service.js

The getProfile and updateProfile functions both return promises from the API routes to the controllers.

Next, let’s create the view edit-account.client.view.html¬†inside public/views/account directory.

edit-account.client.view.html

Now, the profile link on the navbar when clicked should look like this:

screen shot 2016-06-08 at 4 58 04 pm

When the profile information is updated, it should look like this:

screen shot 2016-06-08 at 4 58 21 pm

Step 8: Setting Up Logout

Create a logout.client.controller.js file inside the public/js/controllers directory.

Open the file and add this:

logout.client.controller.js

If you haven’t been logged in and peradventure you get access to that route, it just stops execution else it calls the Satellizer’s $auth ¬†logout function which actually deletes your token from the browser.

screen shot 2016-06-08 at 5 05 55 pm

screen shot 2016-06-08 at 5 06 03 pm

Step 9: Setting Up About

Create a about.client.view.html file inside the public/views/pages directory.

Open the file and add this:

about.client.view.html

Your About page should look like this:

screen shot 2016-06-08 at 7 29 11 am

Conclusion

I know it’s a lot to take in. Stay calm, go over it as many times as you want. A full understanding of the authentication techniques covered today would be great!

Join me in subsequent posts as we continue figuring out how to build our own Youtube :smile:

The source code can be found here. Please do check it out.

You can view how it works in the video below too.

Build Your Own Youtube – Part 2 from unicodeveloper on Vimeo.

Check out part 3 here.

If you have any questions or observations, please drop your thoughts in the comment section below

PROSPER OTEMUYIWA

About PROSPER OTEMUYIWA

Food Ninja, Code Slinger, Technical Trainer, Accidental Writer, Open Source Advocate and Developer Evangelist.