DEV Community

Cover image for Making API Using Node and Express
syed kamruzzaman
syed kamruzzaman

Posted on

Making API Using Node and Express

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

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"

Enter fullscreen mode Exit fullscreen mode

Then create the following folders-

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

Image description

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
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}`)
})

Enter fullscreen mode Exit fullscreen mode

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;

Enter fullscreen mode Exit fullscreen mode

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

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;

Enter fullscreen mode Exit fullscreen mode

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;

Enter fullscreen mode Exit fullscreen mode

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;

Enter fullscreen mode Exit fullscreen mode

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);
}

Enter fullscreen mode Exit fullscreen mode

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);
}

Enter fullscreen mode Exit fullscreen mode

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;

Enter fullscreen mode Exit fullscreen mode

fileFolderName.js

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

export default fileFloderName

Enter fullscreen mode Exit fullscreen mode

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;

Enter fullscreen mode Exit fullscreen mode

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();
};

Enter fullscreen mode Exit fullscreen mode

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();
};

Enter fullscreen mode Exit fullscreen mode

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();
};

Enter fullscreen mode Exit fullscreen mode

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;

Enter fullscreen mode Exit fullscreen mode

here first we define static folder for save image.

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

Enter fullscreen mode Exit fullscreen mode

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!",
        });
        }
    });

Enter fullscreen mode Exit fullscreen mode

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"})
    })

Enter fullscreen mode Exit fullscreen mode

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

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

Enter fullscreen mode Exit fullscreen mode

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}`)
})

Enter fullscreen mode Exit fullscreen mode

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

import authRoute from './authRoute.js';
Enter fullscreen mode Exit fullscreen mode

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;

Enter fullscreen mode Exit fullscreen mode

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;

Enter fullscreen mode Exit fullscreen mode

Image description

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

Image description

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;

Enter fullscreen mode Exit fullscreen mode

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;

Enter fullscreen mode Exit fullscreen mode

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

Image description

Image description

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;

Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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

Image description

Image description

Image description

So, our 1st part is done. Now we will do 2nd part
Scaffolding redux-toolkit in NextJs. Link

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

Top comments (0)