0

I'm having an issue in my NodeJs API and Mongo where I have to Post and Populate with the Username string the field Username.

When I post I get this error:

"TypeError: newPost.save(...).populate is not a function"

Post model:

const mongoose = require("mongoose");

const schema = {
    text: {
        type: String,
        required: true,
        unique: true
    },

    username: {
        type: mongoose.Schema.Types.String,
        ref: "Profile",
        required: true
    },

    image: {
        type: String,
        default: "https://via.placeholder.com/150",
        required: false
    },
    createdAt: {
        type: Date,
        default: Date.now,
        required: false
    },

    updatedAt: {
        type: Date,
        default: Date.now,
        required: false
    }
};

const collectionName = "posts";
const postSchema = mongoose.Schema(schema);
const Post = mongoose.model(collectionName, postSchema);

module.exports = Post;

This is where I'm doing the post method:

postRouter.post("/", async (req, res) => {
    try {
        const newPost = await Posts.create(req.body);

        const username = await Profiles.findOne({
            username: req.body.username
        });

        if (!username) res.status(400).send("Username not found");

        newPost.save().populate(username.username);

        res.send({ success: "Post added", newPost });
    } catch (error) {
        res.status(500).send(error);
        console.log(error);
    }
});

The response output how should be:

{
        "_id": "5d93ac84b86e220017e76ae1", //server generated
        "text": "this is a text 12312 1 3 1",  <<--- THIS IS THE ONLY ONE SENDING"
        "username": "admin",<-- FROM REQ.body or params ??? 
        "createdAt": "2019-10-01T19:44:04.496Z", //server generated
        "updatedAt": "2019-10-01T19:44:04.496Z", //server generated
        "image": ... //server generated on upload, set a default here
    }

I will be also glad to know if the idea of the route like that has sense as the post need to have username if better to have it in the body or in params the request.

SuleymanSah
  • 17,153
  • 5
  • 33
  • 54
Jakub
  • 2,367
  • 6
  • 31
  • 82

3 Answers3

1

When I post I get this error: "TypeError: newPost.save(...).populate is not a function"

You just use populate on the save return type which is (From the API documentation) a Promise. The promise need to be awaited before the populate call.

postRouter.post("/", async (req, res) => {
    try {
        const newPost = await Posts.create(req.body);

        const username = await Profiles.findOne({
            username: req.body.username
        });

        if (!username) res.status(400).send("Username not found");

        const addedPost = await newPost.save();  // <-- Here
        addedPost.populate(username.username);   // <-- Then populate here

        res.send({ success: "Post added", newPost });
    } catch (error) {
        res.status(500).send(error);
        console.log(error);
    }
});

I will be also glad to know if the idea of the route like that has sense as the post need to have username if better to have it in the body or in params the request.

  • Your route seems to be / to create a new post. It could be more clear to name it /post.

  • If you put the username in the request like: /post/:username It make it clear that the post need an username. But you couldn't make another information needed. So, if your application grow and you want another needed parameter you have to make a breaking change in the API route.

Afourcat
  • 81
  • 3
1

You don't need to populate the profile username, because you already have that value in req.body.username.

Also there are a few problems with your code:

1-) You already create a post with Posts.create so there is no need use newPost.save()

2-) If newPost.save() wouldn't need to be removed, you needed to add await.

3-) If newPost.save() wouldn't need to be removed, you can't use populate after save.

4-) You need to return a response in 400 case, with your code, the code will execute when 400 case.

5-) In catch block, you can never see console.log(error)

6-) You are creating a Post, and after you check if username exists in Profile. It is correct to check first if username exists, and only after it is found, then create the post.

So the code must be like this:

postRouter.post("/", async (req, res) => {
  try {
    const { username } = req.body;

    const profile = await Profiles.findOne({ username });

    if (!profile) {
      return res.status(400).send("Username not found");
    }

    let newPost = await Posts.create(req.body);
    newPost.username = username;

    res.send({ success: "Post added", newPost });
  } catch (error) {
    console.log(error);
    res.status(500).send(error);
  }
});
SuleymanSah
  • 17,153
  • 5
  • 33
  • 54
1

try below code considering req.body.username would be mongoose object Id

postRouter.post("/", async (req, res) => {
  try {

    const username = await Posts.findOne({
      username: req.body.username
    });

    if (!username) {
      res.status(400).send("Username not found");
    } else {
      const newPost = await Posts.create(req.body); //might need remove if double entry created
      const post = await Posts.populate(newPost, {
        path: 'username'
      })
      res.send({
        success: "Post added",
        newPost
      });

    }
  } catch (error) {
    res.status(500).send(error);
    console.log(error);
  }
});
p u
  • 1,395
  • 1
  • 17
  • 30