Storing ArrayBuffer in mongodb with mongoose - node.js

I am trying to store the book's array buffer in MongoDB with the help of mongoose. I am getting some error related to validation I hope you guys can help me.
Here is my back-end:
This is my schema
...
const UserSchema = new mongoose.Schema({
book: {
type: Buffer,
},
});
...
router:
...
router.post("/api/book", auth, async (req, res) => {
try {
const { user } = req;
user.book = req.body.book;
await user.save();
res.send({ success: true });
} catch (error) {
console.log(error.name, error.message);
res.send({ success: false, error });
}
});
...
Here is my front-end:
This is the place where I did a post request
...
getBook = async (book) => {
console.log(book);
if (book) {
try {
const res = await axios.post(
"/api/book",
{ book },
{
headers: {
Authorization:
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlYTJlOWJlZmI0OTVhMDZhZTkzZjZmNyIsImlhdCI6MTU4NzczNDk3NH0.fzoJcHbkVhkgYZZgYP0N1XNdqf6uowAVrekntuulOu0",
},
}
);
} catch (error) {
console.log(error.name, error.message);
}
}
...
this is the place where o get the book and converted it into array buffer and passed it to above code
...
handleOnChange = (event) => {
if (event.target.files[0].type === "application/epub+zip") {
let reader = new FileReader();
reader.readAsArrayBuffer(event.target.files[0]);
reader.onload = () => {
this.props.getBook(reader.result);
};
} else {
alert("Only .epub Files are supported");
}
};
...
and here is my error at server side
ValidationError User validation failed: book: Cast to Buffer failed for value "{}" at path "book"
POST /api/book 200 198.131 ms - 533
I think it is because I am using the wrong data type in the mongoose schema

Related

mongodb find all slow

I'm new with nodejs and mongodb. I have a simple request which will return 800 entities without any where statements. Just find all. or explain();
Very slow responder.....
Is there a more better way to do collection.find().lean() ?
const Jobs = require("../model/Jobs.mongo");
const saveData = async (title, link, location, idJob, companyName) => {
const found = await Jobs.findOne({ idJob: idJob });
if (!found) {
try {
const job = new Jobs({
title: title,
link: link,
location: location,
idJob: idJob,
companyName: companyName,
});
await job.save();
console.log(job);
} catch (e) {
console.log(e);
}
} else {
console.log(`${title} ***** is already in the data with id ***** ${idJob}`);
}
};
const getAllJobs = async (req, res) => {
const jobs = await Jobs.find({}).sort({ createdAt: "desc" }).lean();
res.status(200).json({ jobs, count: jobs.length });
};
const getJobByCompany = async (req, res) => {
const {
params: { companyName: companyName },
} = req;
const job = await Jobs.find({
companyName: companyName,
});
if (!job) {
res.status(404).json({});
}
res.status(200).json({ job, count: job.length });
};
module.exports = {
saveData,
getAllJobs,
getJobByCompany,
};
If you are facing this issue for a while try to check you internet connection.
You can also take a look how much is the data that you want to receive.
It can be just from a internet speed drop, let me know what is the result :)

Nodejs exports returns undefined on mongoose Insertion

I have created nodejs application by organising as module structure , The problem I am facing is that a mongodb insertion return undefined value from one of my controller, The issue I found is that my async funtion doesn't wait to complete my mongodb operation But I could not find a solution for that, my route and controller code is given below
route.js
const {
createEvent, editEvent
} = require('./controller');
router.post("/event/create", validateEventManage, isRequestValidated, async(req, res) => {
let data = {};
data.body = req.body;
try{
let event = await createEvent(req.body);
console.log(event) // returned undefined
data.event = event;
res.status(200).json(data);
}catch(error){
console.log(error)
res.status(200).json({error:error});
}
});
controller.js
exports.createEvent = async(data) => {
// return "test" // This works correctly
const eventObj = {
name : data.name,
description : data.desc,
type : data.type,
startDate : new Date()
}
const event = await new Event(eventObj);
await event.save((error,event)=>{
if(error) {
return error;
}
if(event){
return event;
}
});
}
You should not await the new Event constructor.
Also, since you are using async - await you can
remove the callback from the save and try ... catch the error to handle it:
exports.createEvent = async (data) => {
// return "test" // This works correctly
const eventObj = {
name: data.name,
description: data.desc,
type: data.type,
startDate: new Date(),
};
try {
const event = new Event(eventObj);
await event.save();
return event;
} catch (error) {
return error;
}
};

Why is my graphql apollo resolver not being called?

I'm pretty new to graphql (and nodejs as well). I'm following a Udemy course on Apollo and mongo which has been going well mostly. However I can't get one of the resolvers to be called. Another resolver is working fine, and they appear to use the same layout. Also, the context is being called before the resolver that is not being called, so I know it's at least getting that far.
Here is the root server.js with the working context:
const resolvers = require('./resolvers');
...
const apolloServer = new ApolloServer({
typeDefs,
resolvers,
context: async ({ req }) => {
await verifyUser(req);
console.log("=== context ran, user email : ", req.email) ;
return {
email: req.email,
loggedInUserId: req.loggedInUserId
}
}
});
resolvers are modularized, and combined in a /resolvers/index.js, here:
const { GraphQLDateTime } = require('graphql-iso-date')
const userResolver = require('./user');
const taskResolver = require('./task');
const customDateScalarResolver = {
Date: GraphQLDateTime
}
module.exports = [
userResolver,
taskResolver,
customDateScalarResolver
]
and here is the tasks resolver, located at /resolvers/task.js, which is the one not being called:
const uuid = require('uuid')
const { combineResolvers } = require('graphql-resolvers');
const { users, tasks } = require('../constants');
const Task = require('../database/models/task');
const User = require('../database/models/user');
const { isAuthenticated, isTaskOwner } = require('./middleware');
module.exports = {
Query: {
tasks: async ( _, __, { loggedInUserId }) => {
console.log("tasks query, loggedInUserId : ", loggedInUserId);
try {
const tasks = await Task.find( { user: loggedInUserId });
return tasks;
} catch (error) {
console.log(error);
throw error;
}
},
task: async ( parent, { id }, ) => {
console.log("taskbyId query, id : ", id);
// tasks.find(task => task.id == args.id);
try {
const task = await Task.findById(id);
console.log("taskById query, found task? : ", task);
return task;
} catch (error) {
console.log(error);
throw error;
}
},
},
Mutation: {
// createTask: combineResolvers(isAuthenticated, async (_, { input }, { email }) => {
createTask: async (_, { input }, { email }) => {
try {
console.log("creating task, email : ", email);
const user = await User.findOne({ email });
const task = new Task({ ...input, user: user.id });
const result = await task.save();
user.tasks.push(result.id);
await user.save();
return result;
} catch (error) {
console.log(error);
throw error;
}
}
// )
},
Task: {
user: async ( parent ) => {
console.log("in task.user field resolver");
try {
const user = await User.findById(parent.user);
return user;
} catch (error) {
console.log(error);
throw error;
}
}
},
}
When I run the tasks query, the console.log from the context setup function logs 3 times, but does NOT log the console.log line from the tasks resolver. It also appears to not return at all. I'm just using the default graphiql web client. The verifyUser() does find a return a user, so I know the db connection is working fine as well.
mergeResolvers should be used to merge resolvers.
It's designed to merge different [entities] object [/structured] resolvers before use [as one tree structured] in server [config].
F.e. it merges/combines respectively [by type] Query resolvers from users resolver with tasks Query resolvers ... and Mutation resolvers from users resolver with tasks Mutation resolvers.

Asynchronicity issue: why does the second part of my function run before a loop event finishes?

I have a route on an Express server that updates a User profile. The User profile is updated before I have finished to parse the data to update. How so?
I want to update two const: newProfilePicture & newOtherPictures. They are correctly updated, but after the user has been updated, so it's useless. How to fix this asynchronicity issue?
Here is the function:
router.post("/upload-images", upload.array("image"), async (req, res) => {
const { userId } = req.body;
try {
if (req.files) {
let newProfilePicture = null;
let newOtherPictures = [];
req.files.forEach(({ path, originalname }) => {
cloudinary.uploader.upload(
path,
{
resource_type: "image",
public_id: `myapp/users/${userId}/${originalname}`,
crop: "scale",
quality: "auto",
},
(err, res) => {
if (err) {
return fs.unlinkSync("./" + path);
}
fs.unlinkSync("./" + path);
if (originalname === "main") {
return (newProfilePicture = res.secure_url);
}
return newOtherPictures.push({
id: originalname,
url: res.secure_url,
});
}
);
});
// THIS PART IS COMPLETE BEFORE THE req.files.forEach IS DONE
const user = await User.findById(userId);
const { otherPictures, profilePicture } = updatePictures(
newProfilePicture,
newOtherPictures,
user
);
User.findByIdAndUpdate(
userId,
{ profilePicture, otherPictures },
{ new: true }
);
res.send("upload images success");
}
} catch (err) {
console.log("err", err);
return res.status(500).send("upload images failed");
}
});
It happens because cloudinary.uploader.upload() runs asynchronously. Since you mentioned it doesn't have promise interface, you can convert the callback to promise using NodeJS's util.promise function as it's error first callback.
const { promisify } = require("util");
const fs = require("fs");
const cloudinaryUpload = promisify(cloudinary.uploader.upload.bind(cloudinary.uploader))
router.post("/upload-images", upload.array("image"), async (req, res) => {
try {
if (!req.files) {
return res.send("no images in the request body");
}
let newProfilePicture = null;
let newOtherPictures = [];
for (const { path, originalName } of req.files) {
try {
const response = await cloudinaryUpload(path, {
resource_type: "image",
public_id: `myapp/users/${userId}/${originalName}`,
crop: "scale",
quality: "auto",
});
await fs.promises.unlink("./" + path);
if (originalname === "main") {
newProfilePicture = response.secure_url;
continue;
}
newOtherPictures.push({
id: originalName,
url: response.secure_url,
});
} catch (error) {
//do what you want if there is an error
//throw error if you want
await fs.promises.unlink("./" + path);
}
}
const user = await User.findById(userId);
const { otherPictures, profilePicture } = updatePictures(
newProfilePicture,
newOtherPictures,
user
);
//use User.updateOne() as you don't need the doc back
await User.findByIdAndUpdate(
userId,
{ profilePicture, otherPictures },
{ new: true }
);
return res.send("upload images success");
} catch (error) {
console.log("err", err);
return res.status(500).send("upload images failed");
}
});

how can I wait for books data getting from graphql client in async function?

I want to wait for books data getting from graphql query before sending response object
async getUserBookList(req, res) {
let responseObj = {};
const validationRes = this.validateGetUserBookList(req);
const userId = req.params.id;
try {
const userBookList = await dbHelper.filterQuery(
{ user_id: userId },
"userbook"
);
const data = userBookList;
/**
* DESCRIPTION: Gets Books based on bookId
* PARAMS: _id!: string
* RETURNS: books: [Book]
*/
await userBookList.map(book => {
this.fastify.graphQLClient
.executeQuery({
query: books.userBooks({ _id: book.book_id })
})
.then(result => {
// => here the book is getting added
data["books"] = [result.data.books];
console.log(data);
});
});
res.send(data);
} catch (err) {
res.send(err);
}
}
I wanted to know what changes should I do ?? in the code so that response will contain "books" key
await userBookList.map(book => {
this.fastify.graphQLClient
.executeQuery({
query: books.userBooks({ _id: book.book_id })
})
.then(result => {
// => here the book is getting added
data["books"] = [result.data.books];
console.log(data);
});
});
You can use Promis.all with map.
const booksData = await BPromise.all(
userBookList.map( (book) =>
this.fastify.graphQLClient.executeQuery(
{
query: books.userBooks({ _id: book.book_id })
})
));

Resources