DEV Community

loading...
Cover image for Node.js : How to make a REST API part 2

Node.js : How to make a REST API part 2

ridhamz profile image Ridha Mezrigui ・5 min read

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 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);
Enter fullscreen mode Exit fullscreen mode

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
};
Enter fullscreen mode Exit fullscreen mode

don't worry we will take these functions one by one later now we need to setup 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;
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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);
      }
};
Enter fullscreen mode Exit fullscreen mode

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});
        } 
};
Enter fullscreen mode Exit fullscreen mode

the logic is simple this function get the movie id as a parameter and check if it 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'})
    }

};
Enter fullscreen mode Exit fullscreen mode
  • 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'});

     }
};
Enter fullscreen mode Exit fullscreen mode
  • 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'});
    }

};
Enter fullscreen mode Exit fullscreen mode

now we need to build an authentication middleware to protect some of our API routes cuz 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' });
    }
}
Enter fullscreen mode Exit fullscreen mode

let me explain :
when the user make a login or register The API return a token this token indicate 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 want to add a movie the auth meddleware 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 routes 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);
Enter fullscreen mode Exit fullscreen mode

don't forget to import 'auth.js'.

let's test our REST API with postman :

  • first we need to authenticate
    Alt Text

  • add a new movie without add the token in the header
    Alt Text

  • 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)
    Alt Text

  • get movie by id
    Alt Text

  • get all movies
    Alt Text

  • update movie
    Alt Text

  • delete movie
    Alt Text

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.

Discussion

pic
Editor guide
Collapse
gmcamposano profile image
gmcamposano

Very well done mate. Hope to see more from you.