Netflix Clone backend with JWT Authentication

Nabendu Biswas
12 min readDec 21, 2021
Photo by Tezos on Unsplash

We are going to build the backend Netlfix clone in this post. We have earlier created the Admin Dashboard in this post and the frontend ReactJS code created in the earlier post. We are going to use all three in the next post, to create a complete Netflix MERN clone with Admin dashboard.

You can also watch this in video format here.

We will a mern-netflix folder and also create an api folder in which all the backend code will go.

Now, we will start to create our backend API using NodeJS. So, first change to the folder and run the command npm init -y to create an empty node application.

npm init -y

Next, we will add the dependencies in our project using npm from terminal.

npm i express mongoose nodemon dotenv jsonwebtoken

Now, in the package.json file add the script for nodemon, so that we don’t have to restart server after every changes.

package.json

Now, we will first create a cluster in MongoDB Atlas. For that login to your mongodb atlas account and click on New Project button.

New Project

Now, it will ask us to give a name to the database which we are keeping as mern-netflix-db and click on the Next button.

mern netflix

In the next page, click on the Create Project button.

Create Project

Now, we will get a big Build a Database button on which we need to click.

Build Database

In the next page click on the Free tier to get started.

Free

Now, in the Next page choose the Data cluster near to you and click on the Create Cluster button.

Create Cluster

Now, the process have changed a bit and we are allowed to quickstart and give the username and IP address at the beginning only. Click on Finish and Close after giving both.

Finish and Close

Now, we will have our database created finally.

Database created

Now, to get the connection string for our NodeJS application, we will click on Connect your application tab.

Connect

Next, we will get the connection string which we need to copy.

Connection String

Next, we will add the connection string in a .env file.

.env

Now, create an index.js file and import ex[ress, mongoose and dotenv in it. After that through mongoose we are connecting to the database.

Lastly, we are listening to port 8800 for our backend server.

index.js

Now, in the terminal in api folder run the npm start command and we will see the success messages.

npm start

Next, we will create the models which are required in MongoDB. So, create a models folder inside api and User.js file inside it. It will contain the below content, which is basically the way the data will be stored in the UserSchema in our MongoDB database.

User.js

Next, we will create the MovieSchema in Movie.js file. Here, we are storing all the details.

Movie.js

Next, we will create the ListSchema in List.js which basically contains the List of movies or series.

List.js

Now, we will start to create our Routes. First create a routes folder inside the api folder. After that inside it create auth.js file. In this file we are first importing the express router and also the User model.

We will first create a /register endpoint which is a POST request. Here, we are using the User schema and sending the username, email and password in it.

After that inside a try..catch block we are using the save() method from Mongoose to save the data in database.

auth.js

Next, in the index.js file we will add this new route.

index.js

Now, in Postman we will hit the http://localhost:8800/api/auth/register endpoint. Here, we are passing a JSON and did the required changes.

Postman

Now, since this request was successful a new entry for user was created in our MongoDB database.

Data

Now, the problem with this is that the password is clearly visible and if this database is hacked, the hacker gets access to the account. So, first delete this user from MongoDB and then add a package of crypto-js in our backend folder.

npm i crypto-js

We can use many hashing algorithms with it and we are going to use AES. Now update the auth.js file as below. Here, we are also using a SECRET_KEY from the .env file.

auth.js

Now, in the .env file create a SECRET_KEY variable.

.env

Now, again send a POST request and the password will be hashed.

Hashed

Next, we will create the login route in auth.js file. Here, we are first finding the user by email. After that we are decrypting the password and if it is not equal to the req.body.password, we are sending 401 message.

If the password matches, we are sending the user all data expect the password.

auth.js

Now, we will be able to login via postman if we give the correct details.

Login

Next, we are going to implement JWT in auth.js, so that it can be more secure and the login details can also be used in the user browser.

Here, we will have an accessToken from jwt to sign. We are sending the id and the isAdmin also. We also need to pass the SECRET_KEY and also the expiresIn time.

We have also added the accessToken to be sent back in status.

auth.js

Now, when we try to login we will get the accessToken also.

accessToken

Next, we will create our Users routes. This route will also use verification with JWT and for that reason create a verifyToken.js file in the api folder and add the below content in it.

Here, we are getting the token from the request header and splitting it, because we are going to pass Bearer also. After that we are going to verify it with the SECRET_KEY.

verifyToken.js

Now, in the index.js file first add the users route.

index.js

Finally, create an user.js file in the routes folder. Here, add the below code in which we are adding the route to update a user first.

We are first get the id and passing this through the verify to authenticate the JWT token. After that we are using CryptoJS to encrypt the password again. Next inside a try..catch we are using the mongoose function of findByIdAndUpdate to update the data passed in the body.

We also need a new: true to get the latest updated user back.

user.js

Now, to check it from Postman create a new PUT route and pass the user id to it. We also need to pass a token with Bearer and the JWT token from the login route in the Header.

Postman

Next, in the Body we will pass the updated username and hit Send. It will update the user in database and send us back the updated user.

Updated user

Next, we will create the DELETE and the GET single user routes in users.js file. These are simple routes and again using the mongoose methods of findByIdAndDelete and findById, respectively.

users.js

Now, we have added some more users through Postman and from our MongoDB cloud database made one user as admin, by setting isAdmin as true.

isAdmin

Now, we can delete a user by giving the access token of twd user in postman and pass the user id to be deleted.

Deleted

Now, we can also get the data of a particular user back, by giving the id.

Id

Next, we will add two more routes for GET in users.js file. One is to get all the users and other is to get the stats of the users per month.

To get all the users data, we need to be an admin and we have also added the logic to get only the latest 5 users.

users.js

Now, we have updated the createdAt date for some of the users and from Postman did a get request to http://localhost:8800/api/users/stats

We got the data of the users per month.

Users per month

Now, we have added some more users and added the token for the admin. After that when we go to http://localhost:8800/api/users we will get the data for all the users.

To get only latest five users hit the endpoint http://localhost:8800/api/users?new=true

All users

Next, we will create the route for Movies and it will be exactly similar to the Users route. So, create a file movies.js inside the routes folder and add the below content in it.

Here, we are adding the CREATE, UPDATE and DELETE routes. All these routes needed us to be admin and similar to the routes created earlier in users.js file.

const router = require("express").Router();
const Movie = require("../models/Movie");
const verify = require("../verifyToken");
//CREATE
router.post("/", verify, async (req, res) => {
if (req.user.isAdmin) {
const newMovie = new Movie(req.body);
try {
const savedMovie = await newMovie.save();
res.status(201).json(savedMovie);
} catch (err) {
res.status(500).json(err);
}
} else {
res.status(403).json("You are not allowed to add movies!");
}
});
//UPDATE
router.put("/:id", verify, async (req, res) => {
if (req.user.isAdmin) {
try {
const updatedMovie = await Movie.findByIdAndUpdate(
req.params.id, { $set: req.body }, { new: true }
);
res.status(200).json(updatedMovie);
} catch (err) {
res.status(500).json(err);
}
} else {
res.status(403).json("You are not allowed to update movies");
}
});
//DELETE
router.delete("/:id", verify, async (req, res) => {
if (req.user.isAdmin) {
try {
await Movie.findByIdAndDelete(req.params.id);
res.status(200).json("The movie has been deleted...");
} catch (err) {
res.status(500).json(err);
}
} else {
res.status(403).json("You are not allowed to delete movies!");
}
});
module.exports = router;

Next, we will also add the new routes in index.js file.

index.js

Now, we will add the movies through postman. Here, in the Header we have to add the admin token again and the json will contain all the fields from our model file.

Postman

We have also added some series through Postman.

Series

Next, we will also add three routes to get movies, random movies and all movies in movies.js file.

Now, in the random movies, we are using the aggregate function to get movies. We are also using the sample size of 1 here. Here, we can get random series also.

//GET SINGLE MOVIE
router.get("/find/:id", verify, async (req, res) => {
try {
const movie = await Movie.findById(req.params.id);
res.status(200).json(movie);
} catch (err) {
res.status(500).json(err);
}
});
//GET RANDOM MOVIES
router.get("/random", verify, async (req, res) => {
const type = req.query.type;
let movie;
try {
if (type === "series") {
movie = await Movie.aggregate([
{ $match: { isSeries: true } },
{ $sample: { size: 1 } },
]);
} else {
movie = await Movie.aggregate([
{ $match: { isSeries: false } },
{ $sample: { size: 1 } },
]);
}
res.status(200).json(movie);
} catch (err) {
res.status(500).json(err);
}
});
//GET ALL MOVIES
router.get("/", verify, async (req, res) => {
if (req.user.isAdmin) {
try {
const movies = await Movie.find();
res.status(200).json(movies.reverse());
} catch (err) {
res.status(500).json(err);
}
} else {
res.status(403).json("You are not allowed!");
}
});

Now, again in Postman we are giving the admin token and goin to route http://localhost:8800/api/movies/random will give us random movies.

Random Movies

Now, going to http://localhost:8800/api/movies/random?type=series will give us random series.

Random series

Next, we will create the route for Lists. So, create a file lists.js inside the routes folder and add the below content in it.

Again it is quite similar to movies.js and users.js file.

const router = require("express").Router();
const List = require("../models/List");
const verify = require("../verifyToken");
//CREATE
router.post("/", verify, async (req, res) => {
if (req.user.isAdmin) {
const newList = new List(req.body);
try {
const savedList = await newList.save();
res.status(201).json(savedList);
} catch (err) {
res.status(500).json(err);
}
} else {
res.status(403).json("You are not allowed!");
}
});
//DELETE
router.delete("/:id", verify, async (req, res) => {
if (req.user.isAdmin) {
try {
await List.findByIdAndDelete(req.params.id);
res.status(201).json("The list has been delete...");
} catch (err) {
res.status(500).json(err);
}
} else {
res.status(403).json("You are not allowed!");
}
});
//GET
router.get("/", verify, async (req, res) => {
const typeQuery = req.query.type;
const genreQuery = req.query.genre;
let list = [];
try {
if (typeQuery) {
if (genreQuery) {
list = await List.aggregate([
{ $sample: { size: 10 } },
{ $match: { type: typeQuery, genre: genreQuery } },
]);
} else {
list = await List.aggregate([
{ $sample: { size: 10 } },
{ $match: { type: typeQuery } },
]);
}
} else {
list = await List.aggregate([{ $sample: { size: 10 } }]);
}
res.status(200).json(list);
} catch (err) {
res.status(500).json(err);
}
});
module.exports = router;

Now, we will add some list of series from postman. Notice we are passing the id of the series as content.

list of series

We will also add some movies list.

Movies list

The GET routes are also working fine, icluding the one in which we pass the type and genre.

Genre

This completes our long post to create the backend of our Netflix application.

--

--

Nabendu Biswas

Architect, ReactJS & Ecosystem Expert, Youtuber, Blogger