3
\$\begingroup\$

I'm looking for a review of my backend code on how to keep it more DRY. I see a lot of repetition in validation part of email and password, also I need some advice on if I have used the try-catch and async/await correctly. And most importantly, the thing that I have to clear is the sending of response back. Sometimes, I get this error of Can't set headers after they are sent to the client. I know this arises when I'm trying to send two responses back, but I need a review on how do I structure my response sending so that I don't get this error.

Also, as you can see below code that I've used mostly:

 catch (error) {
      return next(error)

but when I tried to do the same in getUserPosts, I got the same error that I've described above. So, I had to change it to:

 catch (error) {
      console.log(error)

I don't know if this a good way to handle errors.


Here's my code with models and controller functions.

models

User.js

const mongoose = require("mongoose")
const bcrypt = require("bcrypt")
const Schema = mongoose.Schema

const userSchema = new Schema({
    username: { type: String, required: true },
    email: { type: String, reuired: true },
    password: { type: String, required: true },
    posts:[{ type: Schema.Types.ObjectId, ref: "Post" }]
}, { timestamps: true })


userSchema.methods.confirmPassword = function (password) {
    return bcrypt.compareSync(password, this.password)
}

const User = mongoose.model("User", userSchema)

module.exports = User

Post.js

const mongoose = require("mongoose");
const Schema = mongoose.Schema;
var URLSlug = require("mongoose-slug-generator");

mongoose.plugin(URLSlug);

const postSchema = new Schema({
  title: { type: String, required: true },
  description: { type: String, required: true },
  user: { type: Schema.Types.ObjectId, ref: "User" },
  slug: { type: String, slug: "title" },
}, { timestamps: true }
)

postSchema.pre("save", function (next) {
  this.slug = this.title.split(" ").join("-");
  next();
});

const Post = mongoose.model("Post", postSchema);

module.exports = Post;

usersController.js

const User = require("../models/User")
const auth = require("../utils/auth")
const validator = require("validator")
const bcrypt = require("bcrypt")

module.exports = {
  registerUser: async (req, res, next) => {
    try {
      var { username, email, password } = req.body
      if (password) {
        const salt = bcrypt.genSaltSync(10)
        password = bcrypt.hashSync(password, salt)
      }
      if (!username || !email || !password) {
        return res
          .status(400)
          .json({ message: "Username, email and password are must" })
      }
      if (!validator.isEmail(email)) {
        return res.status(400).json({ message: "Invaid email" })
      }
      if (password.length < 6) {
        return res
          .status(400)
          .json({ message: "Password should be of at least 6 characters" })
      }
      const user = await User.create({ username, email, password })
      if (!user) {
        return res.status(404).json({ error: "No user found " })
      }
      return res.status(200).json({ user })
    } catch (error) {
      return next(error)
    }
  },

  loginUser: async (req, res, next) => {
    try {
      const { email, password } = req.body
      if (!email || !password) {
        return res.status(400).json({ message: "Email and password are must" })
      }
      if (!validator.isEmail(email)) {
        return res.status(400).json({ message: "Invalid email" })
      }
      const user = await User.findOne({ email })
      if (!user) {
        return res.status(404).json({ message: "This email does not exist" })
      }
      if (!user.confirmPassword(password)) {
        return res.status(401).json({ message: "Incorrect password" })
      }
      const token = auth.signToken({ userId: user._id })

      res.status(200).json({ user, token })
    } catch (error) {
      return next(error)
    }
  },

  identifyUser: async (req, res, next) => {
    try {
      const userId = req.user.userId
      const user = await User.findOne({ _id: userId })
      if (!user) {
        return res.status(500).json({ error: "No user found " })
      }
      return res.status(200).json({ user })
    } catch (error) {
      return next(error)
    }
  },

  getUser: async (req, res, next) => {
    try {
      const user = await User.findById(req.params.id)
      if (!user) {
        return res.status(404).json({ message: "User not found" })
      }
      return res.status(200).json({ user })
    } catch (error) {
      return next(error)
    }
  },

  listUsers: async (req, res, next) => {
    try {
      const users = await User.find({})
      if (!users) {
        return res.status(404).json({ message: "No users found" })
      }
      return res.status(200).json({ users })
    } catch (error) {
      return next(error)
    }
  },

  updateUser: async (req, res, next) => {
    try {
      const userData = {
        username: req.body.username,
        email: req.body.email,
        password: req.body.password,
      }
      const user = await User.findByIdAndUpdate(req.params.id, userData, {
        new: true,
      })
      if (!user) {
        return res.status(400).json({ error: "No user found" })
      }
      return res.status(200).json({ user })
    } catch (error) {
      return next(error)
    }
  },

  deleteUser: async (req, res, next) => {
    try {
      const user = await User.findByIdAndDelete(req.params.id)
      if (!user) {
        return res.status(200).json({ error: "No user found" })
      }
      return res.status(200).json({ user })
    } catch (error) {
      return next(error)
    }
  },

  getUserPosts: async (req, res) => {
    try {
      const user = await User.findById(req.params.id).populate("posts")
      if (!user) {
        return res.status(400).json({ error: "No user" })
      }
      return res.status(200).json({ userPosts: user.posts })
    } catch (error) {
      console.log(error)
    }
  }
}

postController.js

const Post = require("../models/Post")
const User = require("../models/User")
const mongoose = require("mongoose")

module.exports = {
  newPost: async (req, res, next) => {
    try {
      const postData = {
        title: req.body.title,
        description: req.body.description,
        user: req.user.userId,
      }
      const post = await Post.create(postData)
      if (!post) {
        return res.status(404).json({ error: "No post found" })
      }
      const user = await User.findById(req.user.userId)
      user.posts.push(post._id) //pushing post document's objectid to the user's posts array
      user.save().then(() => {
        return res.status(200).json({ user })
      })
    } catch (error) {
      return next(error)
    }
  },

  listPosts: async (req, res, next) => {
    try {
      const posts = await Post.find({}).populate("user")
      if (!posts) {
        return res.status(404).json({ error: "No posts found" })
      }
      return res.status(200).json({ posts })
    } catch (err) {
      return next(error)
    }
  },

  getPost: async (req, res) => {
    try {
      const post = await Post.findById(req.params.id)
      if (!post) {
        return res.status(404).json({ message: "No post found " })
      }
      return res.status(200).json({ post })
    } catch (error) {
      return next(error)
    }
  },

  editPost: async (req, res, next) => {
    try {
      const postData = {
        title: req.body.title,
        description: req.body.description,
      }
      const post = await Post.findByIdAndUpdate(req.params.id, postData, {
        new: true,
      })
      if (!post) {
        return res.status(404).json({ message: "No post found " })
      }
      return res.status(200).json({ post })
    } catch (error) {
      return next(error)
    }
  },

  deletePost: async (req, res) => {
    try {
      const post = await Post.findByIdAndDelete(req.params.id)
      if (!post) {
        return res.status(200).json({ error: "No post found" })
      }
      await User.updateOne(
        { _id: mongoose.Types.ObjectId(post.user) },
        { $pull: { posts: mongoose.Types.ObjectId(post._id) } }
      )
      res.status(200).json({ post })
    } catch (error) {
      console.log(error)
    }
  },
}
```
\$\endgroup\$

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.