Hello community,
in this part we will finish our REST API, if you didn't see the first part you can check it from here.
Let's get started
entire this part we'll cover :
- create new movie
- get movie by ID
- get all movies
- update movie
- delete movie
- finally we will talk about middleware concept and how to build and implement it in our API
first we need to create a movie model, so in the models folder we create a file called movie-model.js :
//movie-model.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const userSchema = new Schema({
title: { type: String, required: true },
description: { type: String, required: true},
creator: { type: String, required: true }
});
module.exports = mongoose.model('Movie', userSchema);
now is the time to create the movie controller in the controllers folder, we create a movie-controller.js file :
//movie-controller.js
const { validationResult } = require('express-validator');
const ObjectId = require('mongoose').Types.ObjectId;
const User = require('../models/user-model');
const Movie = require('../models/movie-model');
// CREATE MOVIE
const createMovie = async (req, res) => {};
// GET MOVIE BY ID
const getMovieById = async (req, res) => {};
// GET ALL MOVIES
const getAllMovies = async (req, res) => {};
// UPDATE MOVIE
const updateMovie = async (req, res) => {};
// DELETE MOVIE
const deleteMovie = async (req, res) => {};
module.exports = {
createMovie,
getMovieById,
getAllMovies,
updateMovie,
deleteMovie
};
don't worry we will take these functions one by one later now we need to set up movie routes we create movie-routes.js
in routes folder :
//movie-routes.js
const { check } = require('express-validator');
const movieController = require('../controllers/movie-controller');
const express = require('express');
const router = express.Router();
const {
createMovie,
getMovieById,
getAllMovies,
updateMovie,
deleteMovie } = movieController;
const validation = [
check('title').not().isEmpty(),
check('description').not().isEmpty(),
check('creator').not().isEmpty()]
// CREATE MOVIE
router.post('/',validation,createMovie);
// GET MOVIE BY ID
router.get('/:id',getMovieById);
// GET ALL MOVIES
router.get('/',getAllMovies);
// UPDATE MOVIE
router.patch('/:id',validation,updateMovie);
// DELETE MOVIE
router.delete('/:id',deleteMovie);
module.exports = router;
the last thing we need to do before we dive into movie-controller.js
functions is to add movie-routes
in the index.js
file :
//index.js
const movieRoutes = require('./routes/movie-routes');
app.use('/api/movie', movieRoutes);
now let's get started with the first function createMovie
:
// CREATE MOVIE
const createMovie = async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty())
return res.status(400).json({ errors: errors.array() });
const { title, description, creator } = req.body;
let existingUser = await User.findOne({ _id: creator });
if (!existingUser)
return res.status(400).json({error : 'invalid user'});
const movie = new Movie({
title,
description,
creator
});
try {
await movie.save();
res.status(200).json({
message: "movie created successfully",
movie: movie
})
} catch (error) {
res.status(500).json(error.message);
}
};
Let me explain the logic first we check the data validation result if some validation failed we return an error otherwise we take the all data title, description and creator = user id if there is no user with this ID also we return an error finally we create the movie object and save it in the database then return a json object contains successfully message and the movie created.
-
getMovieById
:
// GET MOVIE BY ID
const getMovieById = async (req, res) => {
const movieId = req.params.id;
if(!ObjectId.isValid(movieId))
return res.status(400).json({error : 'Invalid id'});
try {
const movie = await Movie.findById(movieId);
if(!movie)
return res.status(404).json('there is no movie with this id.');
res.status(200).json(movie)
} catch (err) {
res.status(500).json({error:err.message});
}
};
The logic is simple this function get the movie ID as a parameter and check if it is a valid ID then we search in the database else return a message that there is no movie with that ID.
-
getAllMovies
:
// GET ALL MOVIES
const getAllMovies = async (req, res) => {
try {
const movies = await Movie.find();
return res.status(200).json(movies)
} catch (err) {
return res.status(500).json({message :'server error'})
}
};
-
updateMovie
:
// UPDATE MOVIE
const updateMovie = async (req, res) => {
const errors = validationResult(req);
if(!errors.isEmpty())
return res.status(400).json({ errors: errors.array() });
const { title, description, creator } = req.body;
const movieId = req.params.id;
if(!ObjectId.isValid(movieId))
return res.status(400).json({error : 'Invalid id'});
try {
const movie = await Movie.findById(movieId);
if(!movie)
return res.status(404).json({message: 'movie not found.'});
await movie.updateOne({
title,
description,
creator
})
return res.status(200).json(movie)
} catch (err) {
console.log(err.message);
res.status(500).json({error : 'server error'});
}
};
-
deleteMovie
:
//DELETE MOVIE
const deleteMovie = async (req, res) => {
const movieId = req.params.id ;
if(!ObjectId.isValid(movieId))
return res.status(400).json({error : 'Invalid id'});
try {
const movie = await Movie.findById(movieId);
if(!movie)
return res.status(404).json({message : 'there is no movie with this id.'});
await movie.remove();
res.status(200).json({message : 'movie removed'});
} catch (err) {
console.log(err.message);
res.status(500).json({error : 'server error'});
}
};
Now we need to build an authentication middleware to protect some of our API routes because in our API anyone can add, update and delete movies without authentication and this is bad.
Middleware functions are functions that have access to the request object (req), the response object (res), and the next middleware function in the application’s request-response cycle. The next middleware function is commonly denoted by a variable named next.
In the middleware folder, create a file called auth.js
:
const jwt = require('jsonwebtoken');
module.exports = function(req, res, next){
const token = req.header('x-auth-token');
if(!token)
return res.status(401).json({ message: 'No token, authorization dnied.'});
try{
const decoded = jwt.verify(token,'supersecretkey');
req.user = decoded.user;
next();
}catch(err){
res.status(401).json({ message: 'Token is not valid' });
}
}
Let me explain :
when the user makes a login or register The API return a token this token indicates that the user is authenticated but to use it we need to add it in the header as 'x-auth-token' with that when the user wants to add a movie the auth middleware get this token from the header and test if it is a valid token then he can add a movie else the middleware return an error that the token not valid.
To protect our API, we just have to import the auth middleware in the movie-routes.js
and add it as a second argument in every route we want to protect.
//movie-routes.js
// CREATE MOVIE
router.post('/',[auth, validation],createMovie);
// UPDATE MOVIE
router.patch('/:id',[auth, validation],updateMovie);
// DELETE MOVIE
router.delete('/:id',auth, deleteMovie);
Don't forget to import 'auth.js'.
Let's test our REST API with postman :
add a new movie with the token in the header (just copy the token and go to header add new key 'x-auth-token' and put the token as a value)
Now we can say that our API is done, we cover all the basics and rules that will you need when you build your own REST API.
Thank you.
Top comments (3)
thanks ...
Very well done mate. Hope to see more from you.
am stuck i connect with my database can you help me?