Making API Using Node and Express
Tags: javascript, express, node, nextjs
Published: 2023-12-17 19:39:58

In this tutorial, we are making API for a Movie Portal. Here we use NodeJs, Express-5.0, and other necessary Packages. We are Trying to Follow the MVC Pattern.

**Step 1 : Scaffolding** 
Open your terminal and type 

```
npm init 
```
Fill up your required field. Then open your visual studio or any kind of editor. Create .env file type this-

```
PORT= 8000
DATABASE_URL = "mongodb://localhost:27017"
IMAGE_BASE_URL = "http://127.0.0.1:8000"

DBNAME = "exress_movie_api"

```
Then create the following folders-

1. controllers
2. db
3. routes
4. helpers
5. middleware
6. public / uploads
     - category
     - movie


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qtxk788esd7zs1puhblg.png)

Now install this app-

1. express
2. cors
3. dotenv
4. joi
5. bcryptjs
6. jsonwebtoken
7. mongoose
8. multer
9. nodemon
10. uuid


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/72hh9259oi5izo9lmtuh.png)
And here, in your package.json file add **“type”: “module”** because we are using express-5.0. if you don’t do that in your .js file import is not work.
After That, create an app.js file in your root folder. Here you type this –

```
import cors from 'cors';
import dotenv from 'dotenv';
import express from 'express';
import connectiondb from './db/connectiondb.js';
dotenv.config();

const app = express();
const port = process.env.PORT || 8000;
const DATABASE_URL = process.env.DATABASE_URL || "mongodb:://localhost:27017";

//connection database
connectiondb(DATABASE_URL);

//app use for different purpose
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended:true}))


app.listen(port, ()=>{
    console.log(`server listening at http://localhost:${port}`)
})

```
Now create db folder inside **connectiondb.js** file and type this-

```
import mongoose from 'mongoose';

//database connection
const connectiondb = async(DATABASE_URL)=>{
    try{
        const options = {
            dbName: process.env.DBNAME
        }

        const connectionResult = await mongoose.connect(DATABASE_URL, options);
        console.log(`connection to mongoDB on database: ${connectionResult.connections[0].name} at ${new Date().toDateString()}`);
    }catch(err){
        console.log(err);
    }
}

export default connectiondb;

```
Now last. Open your MongoDB and Create a Database. Database Name will be **“express_movie_api”** and Collection Name is “movies”;
Now open your terminal and type npm run dev. You can see this-

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/my1krm99l64o571xtk1a.png)

If you see your database name and current date then your connection will be ok. You are now going to the next step.

**Step-2 : Model Defined**
In your models folder make these files.
1. User.js
2. Category.js
3. Movie.js

And Type this. 
**User.js**

```
import mongoose from "mongoose";

const DataSchema = new mongoose.Schema({
    name: {
        type: String,
        trim: true,
        required: true,
        maxlength: 32,
    },
    email: {
        type: String,
        trim: true,
        required: true,
        unique: true,
    },
    password: {
        type: String,
        required: true
    },
},{ timestamps: true, versionKey:false })


const User = mongoose.model('User', DataSchema);

export default User;

```
**Category.js**

```
import mongoose from "mongoose";

const DataSchema = new mongoose.Schema({
    name: {
        type: String,
        required: [true, 'Please enter name'],
        trim: true,
        maxLength: [100, 'Category name cannot exceed 100 characters']
    },
    
    image: { 
        type: Object, 
    },
},{ timestamps: true, versionKey:false })


const Category = mongoose.model('Category', DataSchema);

export default Category;

```

**Movie.js**

```
import mongoose from "mongoose";

const DataSchema = new mongoose.Schema({
    title: {
        type: String,
        required: [true, 'Please enter name'],
        trim: true,
        maxLength: [100, 'Category name cannot exceed 100 characters']
    },
    category_id: {
        type: mongoose.Types.ObjectId,
        ref: "Category",
        required: true
    },
    descriptin: {
        type: String,
        trim: true,
    },
    image: { 
        type: String, 
        required: true
    },
},{ timestamps: true, versionKey:false })


const Movie = mongoose.model('Movie', DataSchema);

export default Movie;

```

**Step-3 : Helpers Function define** 

Now you create some auxiliary function. This functions as some kind of middleware. Create this-
1. bcryp.js
2. jwt.js

**bcryp.js** Function Helping client password encoded and decoded. 

```
import bcrypt from 'bcryptjs';

export const hashMaker = (str)=> {
    const salt = bcrypt.genSaltSync(10);
    return bcrypt.hashSync(str, salt);
}

export const matchData = (str, hash)=> {
    return bcrypt.compareSync(str, hash);
}

```
**jwt.js** function helping Jwt token create and decoded this token. 

```
import jwt from 'jsonwebtoken';
const secret = "@#$%^&*&*"

export const createToken = (payload)=> {
    return jwt.sign(payload, secret, { expiresIn: '1d' }); //1day
}

export const decodeToken = (payload)=> {
    return jwt.verify(payload, secret);
}

```
**Step-4: Making Some Middleware.**
In your middleware folder create some middleware file. Like this-

1. auth.js
2. fileFolderName.js
3. fileUpload.js

**auth.js**

```
import { decodeToken } from '../helpers/jwt.js';

const auth = (req, res, next)=> {
    try {
        const token = req.headers.authorization.split(' ')[1];
        req.user = decodeToken(token);
        //console.log(req.user)
        next();
    } catch (err) {
        res.status(400).send('Authentication Failed')
    }
}
export default auth;

```
**fileFolderName.js**

```
const fileFloderName = (folderName) =>{
    return ( req, res, next)=>{
        req.folderName = folderName;
        next();
    }
}

export default fileFloderName

```

**fileUpload.js**

```
import multer from 'multer';

const storage = multer({
  storage: multer.diskStorage({
    destination: function (req, file, cb) {
      const folderName = req.folderName ?? file.fieldname;
      cb(null, `public/uploads/${folderName}`);
    },
    filename: function (req, file, cb) {
      cb(null, `${Date.now()}-${file.originalname}`);
    },
  }),
  fileFilter: function (req, file, cb) {
    const allowedMimeTypes = ['image/jpeg', 'image/png']; // Specify the allowed image file types

    if (allowedMimeTypes.includes(file.mimetype)) {
      cb(null, true); // Accept the file
    } else {
      cb(new Error('Invalid file type. Only JPEG and PNG files are allowed.')); // Reject the file
    }
  },
  limits: {
    fileSize: 2 * 1024 * 1024, // Set the maximum file size (2MB in this example)
  },
});

export default storage;

```
After that Create validation folder for validation field and then create this file

1. userSingupValidator.js
2. categoryStoreValidator.js
3. movieStoreValidator.js

**userSingupValidator.js** 

```
import Joi from 'joi';

export const userSignupValidator = (req, res, next) => {
  const schema = Joi.object({
    name: Joi.string().required().messages({
      'any.required': 'Name is required',
    }),
    email: Joi.string().email().required().messages({
      'string.email': 'Email must be a valid email address',
      'any.required': 'Email is required',
    }),
    password: Joi.string()
      .min(6)
      .required()
      .pattern(new RegExp('^[a-zA-Z0-9]{3,30}$'))
      .messages({
        'string.min': 'Password must be at least 6 characters',
        'any.required': 'Password is required',
        'string.pattern.base': 'Password must contain only alphanumeric characters',
      }),
      password_confirmation: Joi.string()
      .valid(Joi.ref('password'))
      .required()
      .messages({
        'any.only': 'Password confirmation must match the password',
        'any.required': 'Password confirmation is required',
      }),
      job: Joi.string().optional(),
    
  });

  const { error } = schema.validate(req.body);
  if (error) {
    const errorMessage = error.details[0].message;
    return res.status(400).json({ error: errorMessage });
  }
 
  next();
};

```

**categoryStoreValidator.js**

```
import Joi from 'joi';

export const categoryStoreValidator = (req, res, next) => {
  const schema = Joi.object({
   
    name: Joi.string()
    .required().
    messages({
        'string.name': 'Name is required'
       }),
       
  });

  const { error } = schema.validate(req.body);
  if (error) {
    const errorMessage = error.details[0].message;
    return res.status(400).json({ error: errorMessage });
  }
 
  next();
};

```
**movieStoreValidator.js**

```
import Joi from 'joi';

export const movieStoreValidator = (req, res, next) => {
  const schema = Joi.object({
   
    title: Joi.string()
    .required().
    messages({
        'string.name': 'Name is required'
       }),
    category_id: Joi.string()
    .required().
    messages({
        'string.category_id': 'Category Name is required'
    }), 
    image: Joi.string()
    .required().
    messages({
        'string.image': 'image is required'
    }), 
      
       
  });

  const { error } = schema.validate(req.body);
  if (error) {
    const errorMessage = error.details[0].message;
    return res.status(400).json({ error: errorMessage });
  }
 
  next();
};

```
**Step-5 : Route define**
In your routes folder create this file-

- rootRoute.js
- authRoute.js
- categoryRoute.js
- movieRoute.js

**rootRoute.js** 

In this file you add this code-

```
import express from 'express';
import multer from 'multer';
import CategoryRoute from './CategoryRoute.js';
import authRoute from './authRoute.js';
import movieRoute from './movieRoute.js';

const route = (app)=>{
    app.use(express.static("public/uploads/category"));
    app.use(express.static("public/uploads/movie"));

    app.use("/api", authRoute);
    app.use("/api", CategoryRoute);
    app.use("/api", movieRoute);

    //Multer Error File Handling
    app.use((err, req, res, next) => {
        if (err instanceof multer.MulterError) {
        // Multer-specific errors
        return res.status(418).json({
            err_code: err.code,
            err_message: err.message,
        });
        } else {
        // Handling errors for any other cases from whole application
        return res.status(500).json({
            err_code: 409,
            err_message: "Something went wrong!",
        });
        }
    });

    //unknown route
    app.use('*', (req, res)=>{
        res.status(404).json({status:"fail", data:"Route does not exists"})
    })

}

export default route;

```

here first we define static folder for save image. 

```
app.use(express.static("public/uploads/category"));
app.use(express.static("public/uploads/movie"));

```
Like category image and movie image. Then write error handling image function-

```
//Multer Error File Handling
    app.use((err, req, res, next) => {
        if (err instanceof multer.MulterError) {
        // Multer-specific errors
        return res.status(418).json({
            err_code: err.code,
            err_message: err.message,
        });
        } else {
        // Handling errors for any other cases from whole application
        return res.status(500).json({
            err_code: 409,
            err_message: "Something went wrong!",
        });
        }
    });

```

At the end we write unknown route handler function. If user didn’t write proper url then show  message **“Route does not exists”**

```
//unknown route
    app.use('*', (req, res)=>{
        res.status(404).json({status:"fail", data:"Route does not exists"})
    })

```
Now go to **app.js** file and add this line-

```
import rootRoute from './routes/rootRoute.js';
rootRoute(app);

```
like this way-

```
import cors from 'cors';
import dotenv from 'dotenv';
import express from 'express';
import connectiondb from './db/connectiondb.js';
import rootRoute from './routes/rootRoute.js';
dotenv.config();

const app = express();
const port = process.env.PORT || 8000;
const DATABASE_URL = process.env.DATABASE_URL || "mongodb:://localhost:27017";

//connection database
connectiondb(DATABASE_URL);

//app use for different purpose
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended:true}))

rootRoute(app);

app.listen(port, ()=>{
    console.log(`server listening at http://localhost:${port}`)
})

```
Again we go to **rootRoute.js** file. Here we first discuss about authRoute which we are importing from this

```
import authRoute from './authRoute.js';
```
so, create **authRoute.js** file in the routes folder and write this code- 

```
import express from 'express';
import AuthController from '../controllers/AuthController.js';
import auth from '../middleware/auth.js';
import { userSignupValidator } from '../middleware/validation/userSingupValidator.js';

const router = express.Router();

router.post('/register', userSignupValidator, AuthController.registration);
router.post('/login', AuthController.login);
router.post('/logout', AuthController.logout);
router.post('/change-password', auth, AuthController.changePassword);

export default router;

```

Then go to the controllers folder create **AuthController.js** file and write this-

```
import { hashMaker, matchData } from "../helpers/bcryp.js";
import { createToken } from "../helpers/jwt.js";
import User from "../models/User.js";

class UserController {

  static registration = async (req, res) => {
    try {
      const { name, email, password, password_confirmation } = req.body;
      const existEmail = await User.findOne({ email }).lean().exec();
      if (existEmail) {
        return res.status(413).json({
          code: 201,
          message: "This Email is already used.",
        });
      }

      const hashPassword = hashMaker(password);
      const registerUser = await new User({
        name: name,
        email: email,
        password: hashPassword,
      }).save();
      console.log("userEmail", hashPassword);

      res.status(201).json({
        code: 201,
        message: "User registration Successfully.",
        payload: registerUser,
      });
    } catch (err) {
      console.log(err);
      res.status(500).json({
        code: 500,
        message: "Internal server error.",
      });
    }
  }

  static login = async (req, res) =>{
    try {
        const { email, password } = req.body;
        const existEmail = await User.findOne({ email }).lean().exec();
        //mail check
        if (!existEmail) {
          return res.status(413).json({
            code: 201,
            message: "crediential didn't match",
          });
        }
  
        const hashPassword = matchData(password, existEmail.password);
        //password check
        if(!hashPassword){
            return res.status(413).json({
                code: 201,
                message: "crediential didn't match",
              });
        } 
       
       const {_id, name} = existEmail
        const payload ={
            name,
            email,
            token: "Bearer " + createToken({ _id, email })
        }

        res.status(200).json({
            code: 200,
            message: "User Login Successfully.",
            payload
        });
      } catch (err) {
        console.log(err);
        res.status(500).json({
          code: 500,
          message: "Internal server error.",
        });
      }
  }

  static logout = (req, res)=>{
      try {
        // Clear the token from the cookie
        res.clearCookie('token');
    
        res.status(200).json({
          code: 200,
          message: 'User logged out successfully.',
        });
      } catch (err) {
        console.log(err);
        res.status(500).json({
          code: 500,
          message: "Internal server error.",
        });
      }
   
  }

  static changePassword = async(req, res)=>{
    const {password, password_confirmation } = req.body;
    try{
      const {_id} = req.user;
      if (password !== password_confirmation) {
        return res.status(413).json({
          code: 201,
          message: "crediential didn't match",
        });
      }
      const userInfo = await User.findById(_id);
      const hashPassword = hashMaker(password);
      userInfo.password = hashPassword;
      await userInfo.save();
      console.log(userInfo)
      //await User.updateOne({ _id: _id }, { $set: payload }, { upsert: false });

      res.status(200).json({
        code: 200,
        message: "passwrod Change Successfully.",
        
    });

    }catch(err){
      console.log(err);
      res.status(500).json({
        code: 500,
        message: "Internal server error.",
      });
    }
  }

}

export default UserController;

```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h5qorr1kr3pnqs1ma604.png)

Is your output similar to this congratulation, you have successfully user registration. Now we are testing the login route. 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tuk3hdxnla23zn8xt1h7.png)

If you log in successfully then we can see the token. This token is a jwt token. We can now use this token as an authentication.  

Now we go to again rootRoute.js and this time we are talking about CategoryRoute.js. So create this file and write this

**CategoryRoute.js**

```
import express from 'express';
import CategoryController from '../controllers/CategoryController.js';
import fileFolderName from '../middleware/fileFolderName.js';
import storage from '../middleware/fileUpload.js';
import { categoryStoreValidator } from "../middleware/validation/categoryStoreValidator.js";

const router = express.Router();

router.get("/all-category", CategoryController.allCategory);
router.get("/category-wise-movies", CategoryController.categoryWiseMovie);
router.get("/single-category/:categoryId", CategoryController.singleCategory);

router.post(
  "/category-store",
  //auth,
  categoryStoreValidator,
  fileFolderName("category"),
  storage.fields([{ name: "image", maxCount: 1 }]),
  CategoryController.store
);

router.put(
  "/category-update/:categoryId",
  //auth,
  categoryStoreValidator,
  fileFolderName("category"),
  storage.fields([{ name: "image", maxCount: 1 }]),
  CategoryController.update
);

router.delete(
  "/category-delete/:categoryId",
  //auth,
  //categoryStoreValidator,
  fileFolderName("category"),
  storage.fields([{ name: "image", maxCount: 1 }]),
  CategoryController.delete
);

// //find product by id
router.param("categoryId", CategoryController.categoryById);

export default router;

```
Here you see, we write router.param("categoryId", CategoryController.categoryById). This route is a special route, like when we call single route –
router.get("/single-category/:categoryId", CategoryController.singleCategory);

here this route we passed categoryId param. So this param first calls router. param route and after that call single-category route. It helps for reduce query. 

Now we go to the controllers folder create **CategoryController.js** and write this

```
import fs from "fs";
import Category from "../models/Category.js";
import Movie from "../models/Movie.js";

class CategoryController {
  //find a Category by  id
  static categoryById = async (req, res, next, id) => {
    const category = await Category.findById(id);

    if (!category) {
      return res.status(404).json({
        code: 404,
        message: "Category not found.",
      });
    }

    req.category = category;
    // console.log('cat result', req.category)
    next();
  };

  //all Category
  static allCategory = async (req, res) => {
    try {
      const imageBaseURL = process.env.IMAGE_BASE_URL;
      const allCategory = await Category.find();

      // Update the image URL for each category
      const updatedCategoryList = allCategory.map((category) => {
        return {
          ...category._doc,
          image: `${imageBaseURL}/${category.image}`,
        };
      });

      res.status(200).json({
        message: "All Category",
        data: updatedCategoryList,
      });
    } catch (err) {
      console.log(err);
      res.status(500).json({
        code: 500,
        message: "Internal server error.",
      });
    }
  };

  //Category wise movies
  static categoryWiseMovie = async (req, res) => {
    try {
      const imageBaseURL = process.env.IMAGE_BASE_URL;
      const allCategories = await Category.find();

      // Update the image URL for each category
      const updatedCategoryList = await Promise.all(allCategories.map(async (category) => {
        // Find all movies related to this category
        const movies = await Movie.find({ category_id: category._id });

        // Update the image URL for each movie
        const updatedMovies = movies.map((movie) => {
          return {
            ...movie._doc,
            image: `${imageBaseURL}/${movie.image}`,
          };
        });

        return {
          ...category._doc,
          image: category.image ? `${imageBaseURL}/${category.image}` : null,
          movies: updatedMovies,
        };
      }));

      res.status(200).json({
        message: "Category-wise Movies",
        data: updatedCategoryList,
      });
    } catch (err) {
      console.log(err);
      res.status(500).json({
        code: 500,
        message: "Internal server error.",
      });
    }
};

  //singel Category
  static singleCategory = async (req, res) => {
    const imageBaseURL = process.env.IMAGE_BASE_URL;
    const category = req.category;
    category.image = `${imageBaseURL}/${category.image}`;
    res.status(200).json({
      message: "Single Category",
      data: category,
    });
  };

  //store category
  static store = async (req, res) => {
    const { name } = req.body;
    const image = req.files.image[0].filename;

    try {
      // const categoryInfo = await new Category({
      //     name,
      //     image: image ?? ''
      // }).save()
      console.log("controller req", image);

      const categoryInfo = await Category.create({
        name,
        image: image,
      });

      res.status(200).json({
        message: "Category Create Successfully",
        data: categoryInfo,
      });
    } catch (err) {
      res.status(500).json({
        code: 500,
        message: "Internal server error.",
      });
    }
  };

  //Category update or edit
  static update = async (req, res) => {
    const { name } = req.body;
    const imageBaseURL = process.env.IMAGE_BASE_URL;
    let image = null; // Initialize image as null
    let existingCategory = req.category;
    try {
      if (req.files && req.files.image) {
        // If a new image then delete the old image
        if (existingCategory && existingCategory.image) {
          fs.unlinkSync(`public/uploads/category/${existingCategory.image}`);
        }
        // Save the new image
        image = req.files.image[0].filename;
      }

      existingCategory.name = name ? name : existingCategory.name;
      existingCategory.image = image ? image : existingCategory.image;
      existingCategory.save();

      // Update the image URL with the base URL
      const updateCategory = await Category.findById(existingCategory._id);
      updateCategory.image = `${imageBaseURL}/${updateCategory.image}`;

      res.status(200).json({
        message: "Category updated successfully.",
        data: updateCategory,
      });
    } catch (err) {
      console.log(err);
      res.status(500).json({
        code: 500,
        message: "Internal server error.",
      });
    }
  };

  //delete category
  static delete = async (req, res) => {
    try {
      // Find the category by ID
      const category = req.category;

      // Delete the category's image file if it exists
      if (category.image) {
        fs.unlinkSync(`public/uploads/category/${category.image}`);
      }

      // Delete the category from the database
      await Category.deleteOne({ _id: category._id });

      res.status(200).json({
        message: "Category deleted successfully.",
      });
    } catch (err) {
      console.log(err);
      res.status(500).json({
        code: 500,
        message: "Internal server error.",
      });
    }
  };
}

export default CategoryController;

```
Now again open your postman and test this api like this way


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/udzrly97lbj3p85fzwsg.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6dh5gzkl9py3gy3mgjgq.png)

Now we are discussing movie route. So go to routes folder and create this-

**movieRoute.js**

```
import express from "express";
import MovieController from "../controllers/MovieController.js";
import fileFolderName from "../middleware/fileFolderName.js";
import storage from "../middleware/fileUpload.js";
import { movieStoreValidator } from "../middleware/validation/movieStoreValidator.js";
import auth from '../middleware/auth.js';

const router = express.Router();

router.get("/all-movies", MovieController.allMovie);
router.get("/top-movies", MovieController.topMovie);
router.get("/single-movie/:movieId", MovieController.singleMovie);

  router.post(
    "/movie-store",
    //auth,
    movieStoreValidator,
    fileFolderName("movie"),
    storage.fields([{ name: "image", maxCount: 1 }]),
    MovieController.store
  );

  router.post(
    "/ai-movie-store",
    //auth,
    fileFolderName("movie"),
    storage.fields([{ name: "image", maxCount: 1 }]),
    MovieController.aiStore
  );
  
  router.put(
    "/movie-update/:movieId",
    //auth,
    fileFolderName("movie"),
    storage.fields([{ name: "image", maxCount: 1 }]),
    MovieController.update
  );
  
  router.delete(
    "/movie-delete/:movieId",
    //auth,
    MovieController.delete
  );
  
//   //find product by id
 router.param("movieId", MovieController.movieById);

  export default router;

```
and then go to controllers folder and create this-
**MovieController.js**

```
import fs from "fs";
import { v4 } from "uuid";
import Movie from '../models/Movie.js';

class MovieController{
  //find a movie by  id
  static movieById = async (req, res, next, id) => {
    const movie = await Movie.findById(id);
    console.log('movie info', movie)
    if (!movie) {
      return res.status(404).json({
        code: 404,
        message: "Movie not found.",
      });
    }

    req.movie = movie;
    // console.log('cat result', req.category)
    next();
  };

  //all Movie
  static allMovie = async (req, res) => {
    try {
      const imageBaseURL = process.env.IMAGE_BASE_URL;
      const allMovie = await Movie.find().populate('category_id');

      // Update the image URL for each category
      const updatedMovieList = allMovie.map((movie) => {
      
        return {
            ...movie._doc,
            image: `${imageBaseURL}/${movie.image}`,
            category_id: {
              ...movie.category_id._doc,
              image: `${imageBaseURL}/${movie.category_id.image}`,
            },
          };
      });

      res.status(200).json({
        message: "All Movie",
        data: updatedMovieList,
      });
    } catch (err) {
      console.log(err);
      res.status(500).json({
        code: 500,
        message: "Internal server error.",
      });
    }
  };

  //Top Movie
  static topMovie = async (req, res) => {
    try {
      const imageBaseURL = process.env.IMAGE_BASE_URL;
      const topMovies = await Movie.find()
        .sort({ createdAt: -1 }) 
        .populate('category_id')
        .limit(8); 

      // Update the image URL for each movie
      const updatedTopMovies = topMovies.map((movie) => {
        return {
          ...movie._doc,
          image: `${imageBaseURL}/${movie.image}`,
          category_id: {
            ...movie.category_id._doc,
            image: `${imageBaseURL}/${movie.category_id.image}`,
          },
        };
      });

      res.status(200).json({
        message: "Top 8 Movies",
        data: updatedTopMovies,
      });
    } catch (err) {
      console.log(err);
      res.status(500).json({
        code: 500,
        message: "Internal server error.",
      });
    }
  };

  //singel Category
  static singleMovie = async (req, res) => {
    const imageBaseURL = process.env.IMAGE_BASE_URL;
    const movie = req.movie;
    await movie.populate('category_id');
    movie.image = `${imageBaseURL}/${movie.image}`;
    movie.category_id.image = `${imageBaseURL}/${movie.category_id.image}`;
    res.status(200).json({
      message: "Single movie",
      data: movie,
    });
  };

   //store Movie
   static store = async (req, res) => {
    const { title, discription, category_id } = req.body;
    const image = req.files.image[0].filename;

    try {
        const movieInfo = await Movie.create({
        title,
        discription,
        category_id,
        image: image,
      });

      res.status(200).json({
        message: "Movie Create Successfully",
        data: movieInfo,
      });
    } catch (err) {
      res.status(500).json({
        code: 500,
        message: "Internal server error.",
      });
    }
  };

  //Ai movie store
  static aiStore = async (req, res) => {
   
    const { title, category_id, description, image } = req.body;
    
    console.log('node-1', req.body)
    
    
    // Convert the base64 image data to a buffer
    const imageBuffer = Buffer.from(image, 'base64');
    
    // Generate a unique filename for the image using UUID
    const imageName = `${v4()}.jpg`;
  
    try {
      // Save the image to a directory (you can change the path as needed)
      fs.writeFileSync(`./public/uploads/movie/${imageName}`, imageBuffer);
  
      // Save the movie information to the database
      const movieInfo = await Movie.create({
        title,
        description,
        category_id,
        image: imageName,
      });
  
      res.status(200).json({
        message: "Movie Create Successfully",
        data: movieInfo,
      });
    } catch (err) {
      res.status(500).json({
        code: 500,
        message: "Internal server error.",
      });
    }
  };

  //Category update or edit
  static update = async (req, res) => {
    const { title, discription, category_id } = req.body;
    const imageBaseURL = process.env.IMAGE_BASE_URL;
    let image = null; // Initialize image as null
    let existingMovie = req.movie;
    try {
      if (req.files && req.files.image) {
        // If a new image then delete the old image
        if (existingMovie && existingMovie.image) {
          fs.unlinkSync(`public/uploads/movie/${existingMovie.image}`);
        }
        // Save the new image
        image = req.files.image[0].filename;
      }
      console.log('movie data', title, discription, category_id)
      existingMovie.title = title ? title : existingMovie.title;
      existingMovie.discription = discription ? discription : existingMovie.discription;
      existingMovie.category_id = category_id ? category_id : existingMovie.category_id;
      existingMovie.image = image ? image : existingMovie.image;
      existingMovie.save();

      console.log("existingMovie", existingMovie)
      // Update the image URL with the base URL
      const updateMovie = await Movie.findById(existingMovie._id);
      updateMovie.image = `${imageBaseURL}/${updateMovie.image}`;

      res.status(200).json({
        message: "Movie updated successfully.",
        data: updateMovie,
      });
    } catch (err) {
      console.log(err);
      res.status(500).json({
        code: 500,
        message: "Internal server error.",
      });
    }
  };

  //delete category
  static delete = async (req, res) => {
    try {
      // Find the category by ID
      const movie = req.movie;

      // Delete the category's image file if it exists
      if (movie.image) {
        fs.unlinkSync(`public/uploads/movie/${movie.image}`);
      }

      // Delete the category from the database
      await movie.deleteOne({ _id: movie._id });

      res.status(200).json({
        message: "movie deleted successfully.",
      });
    } catch (err) {
      console.log(err);
      res.status(500).json({
        code: 500,
        message: "Internal server error.",
      });
    }
  };

}

export default MovieController

```
Now we do finish touch. Open postman and test it,  like this-


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rxsb7cj4xv5vviw4m4w9.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zhk1eq3mdrw6ulr7kpgv.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qqde1cdyqdbtof1ueo5h.png)

So, our 1st part is done. Now we will do 2nd part
Scaffolding redux-toolkit in NextJs. [Link](https://dev.to/kamruzzaman/scaffolding-redux-toolkit-in-nextjs-1n2p)

full Project github
**Node**
https://github.com/kamruzzamanripon/node-movie-api
**NextJs**
https://github.com/kamruzzamanripon/next-movie-ui-with-node-api
**NextJs UI**
https://github.com/kamruzzamanripon/next-movie-ui