Node.js : REST API part 2
Tags: node, express, mongodb, api
Published: 2020-10-19 19:35:17

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](https://dev.to/ridhamz/node-js-how-to-make-rest-api-part-1-37mb).

# 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 :
```javascript
//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 :
```javascript
//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 :
```javascript
//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 :
```javascript
//index.js
const movieRoutes = require('./routes/movie-routes'); 

app.use('/api/movie', movieRoutes);
```
now let's get started with the first function `createMovie` :
```javascript
// 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` :
```javascript
// 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` :
```javascript
// 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`:
```javascript
// 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` :
```javascript
//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` :
```javascript
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.

```javascript
//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 :
* first we need to authenticate 
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/6snfvgpnaqcnvqijfhuk.png)

* add a new movie without add the token in the header 
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ph86jx991txx3jykyfmr.png)

* 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](https://dev-to-uploads.s3.amazonaws.com/i/3maf7lhfc0r8m6vajmlv.png)

* get movie by ID 
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/jvarjrvn3tioo346cqe5.png)

* get all movies 
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/yxd573ws31oq1jclpyww.png)

* update movie 
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/p2gk0l60itp41yxgv1jy.png)

* delete movie 
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/vv2achb7z24x0w13zjqw.png)

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.