How to upload image to mongodb using node.js, express.js - node.js

Am trying to upload image to the task collection in my mongodb but keep getting an error message.
here is my task model
const mongoose = require('mongoose')
const taskSchema = new mongoose.Schema({
description: {
type: String,
trim: true,
required: true
},
completed: {
type: Boolean,
default: false
},
owner: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User'
},
avatar: {
type: Buffer
}
},
{
timestamps: true
})
const Tasks = mongoose.model('Tasks', taskSchema )
module.exports = Tasks
Here is my task router code
router.post('/tasks/me/avatar', auth, upload.single('avatar'), async (req, res) => {
const buffer = await sharp(req.file.buffer).resize({ width: 250, height: 250 }).png().toBuffer()
req.tasks.avatar = buffer
await req.tasks.save()
res.send()
}, (error, req, res, next) => {
res.status(400).send({ error: error.message })
})
error message
[![enter image description here][1]][1]
It keeps giving me an error message of cannot set property 'avatar' of undefined, meanwhile the avatar field has already been defined in the task model.
How can I fix this issue, thanks.

Ok after looking at your answer i'll propose this solution.
Instead of:
req.tasks.avatar = buffer
await req.tasks.save()
Try this
const query = {id: docId}; //prepare a query to find the doc
Task.findOneAndUpdate(query, { $set: {avatar: buffer} }, ()=>{
/Your callback function to run after completed
});
//Then call findOneAndUpdate method from any mongoose.model
//Which will do exactly what its name says:
//Finding ONE element, the first one matching your query
//And then update any values inside $set
//And after that you may use any callback function
Let me know if that helps you out.

Related

Update multiple documents instance in MongoDb collection with Mongoose. save() is not a function

Mongoose newbe here. I got the following function to update the references (deleting them) in the document Post when a Tag is deleted. When I call my GraphQl API this is what I got:
message": "posts.save is not a function"
The function in my gql resolver:
async deleteTag(root, { id }, context) {
const posts = await Post.find();
const tag = await Tag.findById(id);
if(!tag){
const error = new Error('Tag not found!');
error.code = 404;
throw error;
}
posts?.forEach(async (post) => {
await post.tags.pull(id);
})
await posts.save()
await Tag.findByIdAndRemove(id);
return true;
}
This is the Post model:
const PostSchema = new Schema({
body: {
type: String,
required: true
},
tags: {
type: [Schema.Types.ObjectId],
ref: 'Tag',
required: false
},
});
and this is the Tag model:
const TagSchema = new Schema(
{
name: {
type: String,
required: true
},
},
{ timestamps: true }
);
Looks like I can't call the method save() on the array of objects returned by Exercise.find()
I used the same pattern in other functions, the difference is that there I used .findById()
Any solution? Advice and best practice advide are super welcome.
You have to save the posts individually:
posts?.forEach(async (post) => {
await post.tags.pull(id);
await post.save();
})
Or use Model.updateMany() combined with the $pull operator.
FWIW, you should probably limit the number of matching Post documents by selecting only documents that have the specific tag listed:
await Post.find({ 'tags._id' : id });

create mongoose schema with array of objects

Even though the question have been asked numerous time none of the answers have any idea to help me .
This is my mongoose Schema
const mongoose = require('mongoose')
const { Schema } = mongoose;
const recipeSchema = new Schema({
name: { type: String, required: true },
description: { type: String, required: true },
imagePath: { type: String, required: true },
ingredients:[
{
name:{type:String, required:true},
amount:{type:Number,required:true }
}
]
})
module.exports = mongoose.model("Recipe",recipeSchema);
what i need is to get the data from angular and store it to my database using node
const Recipe = require('../models/recipe.model');
const recipeCtrl={};
recipeCtrl.CreateRecipeServer =async(req, res, next)=>{
if(!req.file) {
return res.status(500).send({ message: 'Upload fail'});
}
else {
let ingredientArray=new Array()
ingredientArray.push(req.body.ingredients)
req.body.imageUrl = 'http://192.168.0.7:3000/images/' + req.file.filename;
const recipe=new Recipe({
name:req.body.name,
description:req.body.description,
imagePath:req.body.imageUrl,
ingredients:[
{
name:ingredientArray,
amount:ingredientArray }
]
});
await recipe.save();
}
Everything except the ingredients array works perfectly/as i require.
I am getting the ingredients as an array from formdata so it have to be JSON.stringfied inorder to append with the form. So what i am getting at backend is string . eg
**[{"name":"dasdasd","amount":2},{"name":"fsfsd","amount":2},{"name":"sdfsdgd","amount":3}]**
this is a string. Any ideas on how to convert it and store to database
use JSON.parse and choose first element of that
JSON.parse(data)[0]

Node.js: Cast to ObjectId failed for value "new" (type string)

I am rather new to node and am having an issue that is a bit difficult for me to decipher. I am trying to make a reddit clone. The part that I am working on right now is to allow users to view posts other users have posted. I was trying to implement a new route by pasting in this bit of code
app.get('/', (req, res) => {
Post.find({}).lean()
.then((posts) => res.render('posts-index', { posts }))
.catch((err) => {
console.log(err.message);
})
})
However, I am getting this error when I run it:
Cast to ObjectId failed for value "new" (type string) at path "_id" for model "Post"
It seems like something is wrong with my post.js model, but I can't identify it. Could someone review this and help me identify the triggering factor?
const { Schema, model } = require('mongoose');
const postSchema = new Schema({
title: { type: String, required: true },
url: { type: String, required: true },
summary: { type: String, required: true },
}, { timestamps: true });
module.exports = model('Post', postSchema);

_doc in request body

I am trying to send an object created by MongoDB model as a body of post request in a jest test.
Here is the test:
test("post request successfully creates a new blog", async () => {
const newBlog = new Blog({
title: "My mundane day at home 3",
author: "AbG",
url: "https://www.google.co.in/",
likes: 11,
});
await api
.post("/api/blogs")
.send(newBlog)
.expect(201)
.expect("Content-Type", /application\/json/)
.catch((err) => console.log(err));
const blogs = await Blog.find({});
expect(blogs).toHaveLength(initialBlogs.length + 1);
});
As you can see, I am sending the newBlog as a body of request. But when I receive it in controller, the newBlog is present in the request.body._doc instead of request.body.
I think this has something to do with the mongoose model of blog.
const blogSchema = new mongoose.Schema({
title: {
type: String,
required: true,
},
author: {
type: String,
required: true,
},
url: {
type: String,
required: true,
},
likes: {
type: Number,
required: false,
default: 0,
},
});
module.exports = mongoose.model("Blog", blogSchema);
I cannot understand why this is happening.
I found out where I was going wrong.
In the test case, I was creating a mongodb schema instance and passing it as body of request instead of a plain JS object. And in the express app, I was trying to access the request body as a JS object. So that created a confusion.
The changes that I did:
In test:
Instead of
const newBlog = new Blog({....})
I did
const newBlog = {....}
So, I avoided passing mongodb object as request body and created the mongodb object using the constructor immediately before where I needed it.
Here is the post route:
blogsRouter.post("/", async (request, response) => {
const blog = new Blog(request.body);
const savedBlog = await blog.save();
response.status(201).json(savedBlog);
});

How do I update a sub-document in MongoDB without querying the parent?

I have the following MongoDB Schema:
let ChannelSchema = new Schema({
name: {type: String},
active: { type: Boolean, default: true },
}, { timestamps: true })
let UserSchema = new Schema({
username: {type: String, required: [true, "username_required"], unique: true},
channel: [ ChannelSchema ]
}, { timestamps: true });
I am trying to create an endpoint that toggles the active status of my Channel schema. However, when I created the following test code the response is undefined
router.post('/deactivate', async (req, res, next) => {
let channelId = req.body.channelId;
let channel = await Channel.findOneById(channelId});
res.json(channel);
});
How do I select a channel by its id, in this instance, and change the active status to false? I would prefer not to have to select the User document first. Is that possible?
If you using mongoose as ORM you should use findById instead of findOneById
let channel = await Channel.findById(channelId});
This is what ended up working for me:
router.post('/deactivate', async (req, res, next) => {
let channelId = req.body.channelId;
let channel = await User.findOneAndUpdate({'channel._id': channelId}, { 'channel.$': 1 });
res.json(channel);
});
I would explain it but I don't understand completely why it works just yet.

Resources