I'm trying to save Form Data with its information but when I do so I get a weird looking object that I think the server is refusing to accept and returns a 500 error.
Here is how I'm handling it:
const formData = new FormData();
let currentlyUploaded = e.target.files[0];
formData.append(currentlyUploaded.name, currentlyUploaded);
// passing the data in function then ...
And this is how it looks when I console it later before the call, and it doesn't look correct.
here is the route:
router.post(
`${prefix}/create-quiz`,
userAuth,
upload.single("file"),
async (req, res) => {
try {
const user = await User.findOne({ _id: req.params.userId });
const newQuiz = new Quiz({
purchaseCoins: req.body.purchaseCoins,
privacy: req.body.privacy,
created_by: req.params.userId,
category: req.body.category,
title: req.body.title,
description: req.body.description,
difficulty: req.body.difficulty,
questions: req.body.questions,
thumbnail: req.file.filename,
});
await newQuiz.save();
user.quizzes.push(newQuiz._id);
await user.save();
return res.status(201).json({
message: "Finally , quiz created properly!",
success: true,
});
} catch (error) {
// return res.status(500).json({
// message: "Can't save this quiz try again, check if it already exists",
// success: false,
// });
console.log("errorium: ", error)
}
}
);
Related
I am making a social media backend.
I save post added by the used in a Post model and user data in a User model.
GITHUB_REPO_LINK_AT_END
NOTE: UserSchema have a Schema.TypesOf.ObjectId Reference To POST Model. User_Model_&_Post_Model_are_provided_in_the_end
To get all posts of a particular user, I make a GET request to the route "/post" with body:
{ "id" : "6399d54c00308a2fe0bdf9fc"} //sending user id to fetct all the ID of the post from USER model, so i can then query the POST model for the posts
This the function I am getting problem with:
const getPost = async(req, res)=>{
if(req.body.id){
try {
const user = await User.findById(req.body.id).select('-_id post');
//THIS IS THE PART I NEED HELP WITH-------------------------------------------
const posts = await user.post.map(async(postID) => {
const result = await Post.findById(postID).select('-_id title body');
//console.log(result) THIS PRINTS THE CORRECT OBJ FROM DB
return result; //THIS RETURNS AN EMPTY OBJECT HERE
});
//----------------------------------------------------------------------------
res.status(200).json(posts);
} catch (error) {
console.log(error);
res.status(500).json({message: error.message});
}
}
};
when sending a GET request it returns an empty array with empty objects.//PS: no. of empty obj = actual no. of obj in DB
//This is the response
[{},{},{},{},{},{},{},{},{},{},{}]
{
//This is the user object
"_id": "6399d54c00308a2fe0bdf9fc",
"createdAt": "2022-12-14T13:52:40.483Z",
"name": "ShivamUttam",
"username": "Fadedrifleman",
"post": [
"6399d57200308a2fe0bdfa00",
"6399d5c400308a2fe0bdfa06",
"6399d5ca00308a2fe0bdfa0a",
"6399d5d600308a2fe0bdfa0e",
"6399de29e8aa8697299941c5",
"6399dec6e9b79ac66c59cd7a",
"6399df0dbea937f8b3365979",
"6399df31bea937f8b336597d",
"6399df31bea937f8b3365981",
"6399df32bea937f8b3365985",
"6399df33bea937f8b3365989"
],
"__v": 5
}
Model for USER and POST:
User:
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
createdAt: {
type: Date,
default: Date.now()
},
name: {
type: String,
required: [true, 'name must be provided'],
},
username : {
type: String,
required: [true, 'Username must be provided'],
},
post:[{
type: mongoose.Schema.Types.ObjectId,
ref: 'Post',
}],
});
module.exports = mongoose.model('User', userSchema)
Post:
const mongoose = require('mongoose')
const postSchema = new mongoose.Schema({
createdAt: {
type: Date,
default: Date.now()
},
title:{
type: String,
required: [true, "title cannot be empty"],
max: [20, "title cannot exceed 20 character"]
},
body: {
type: String,
max: [145, "body cannot exceed 145 character"],
},
tags:{
type: String,
},
});
module.exports = mongoose.model('Post', postSchema);
https://github.com/Fadedrifleman/socialMediaAppBackend/tree/master
Since you have used async callback function in the map method, a async function always return a promise, whatever the entity is returned by the function is wrapped inside a promise and that promise is returned.
If you want to use map function with async js code, you can try the following
const posts = await Promise.all(user.post.map(async(id)=>{
const result = await Post.findById(postID).select('-_id title body');
return result;
}));
and if you want to straightaway send the posts, you can also use .lean() method on posts, as in
await Post.findById(postID).select('-_id title body').lean()
You had some bugs that probably would interfere, I did a pull request to fix them: https://github.com/Fadedrifleman/socialMediaAppBackend/pull/1
But the main part would be this:
const getPost = async (req, res) => {
try {
if (req.body.id) {
const user = await User.findById(req.body.id);
await user.populate("post");
res.status(200).json(user.post);
return;
}
const posts = await Post.find({ access: 'public' }).select('-access');
res.status(200).json(posts);
} catch (error) {
res.status(500).json({ message: error.message });
}
};
I'm developing a MERN chat app and trying to replicate the feature which WhatsApp provides -> message sent/seen status, so for that I need to check whether the message I/user sent is successfully synced/updated/created in my MongoDB, I know similar functionality can be achieved in AWS Amplify using the event outboxMutationEnqueued which says Dispatched when a local change has been newly staged for synchronization with the cloud, so it works like whenever we are trying to push something to synchronize with the cloud, this event is going to be fired, and once it is finished, outboxMutationProcessed is going to be triggered which says Dispatched when a local change has finished syncrhonization with the cloud and is updated locally.
So we can listen to these events whenever we are trying to send a message, and once our message mutation is processed we are going to receive outboxMutationProcessed, and then we can update the status of the message to sent or single tick or delivered.
import Amplify, {Hub} from 'aws-amplify';
useEffect(() => {
const listener = Hub.listen('datastore', async (hubData) => {
const {event, data} = hubData.payload;
if (event === 'networkStatus') {
console.log('User has a network connection: ', data.active);
}
if (event === 'outboxMutationProcessed') {
if (data.model === Message)
console.log('Mutation has been synced with the cloud: ', data);
// set the message status to delivered.
}
})
return () => listener();
}, []);
So, the question, do we have something similar in MongoDB? I'm currently using React Native, Node, Express, Sockets, Mongoose, MongoDB.
Currently, my API end point and collections (for creating a new message and saving in to db):
I have 3 collections: users, 'messages', 'chatRooms'.
const mongoose = require('mongoose');
const MessageSchema = mongoose.Schema({
roomId: String,
senderId: String,
text: String,
status: String, // INQUEUE, SENT, DELIVERED, READ
}, {
timestamps: true,
});
module.exports = mongoose.model('Message', MessageSchema);
router.post('/create_message', checkAuth, async (req, res) => {
const {chatRoomId, senderId, text} = req.body;
try {
const message = new Message({
chatRoomId,
senderId,
text,
});
const result = await message.save();
return res.status(200).json({
type: 'success',
data: result,
});
} catch (error) {
return res.status(422).send({error: `${error.message}`});
}
});
// SOCKET IMPLEMENTATION FOR REALTIME FEATURE
socket.on('listener', async data => {
io.to(id).emit('new_message', data);
const message = new Message({
chatRoomId: data.roomId,
senderId: data.senderId,
text: data.text,
status: data.status, // data.status = 'INQUEUE'
});
await message.save();
// maybe something here...? not sure
// data.status = 'SENT' after successful creation of document.
});
Maybe an event which we can fire, during the await message.save(...something here...), and if it is successfully saved in our DB, we can send it to our frontend or using socket?
If anyone could provide an example, it would be really helpful!
Edit: I changed this up a bit.
schemas:
const UserSchema = new mongoose.Schema({
name: String,
online: Boolean,
isActive: Boolean,
})
const RoomSchema = new mongoose.Schema({
name: String,
members: [{ type: mongoose.Schema.Types.ObjectId, ref: 'users' }],
})
const MessageSchema = new mongoose.Schema({
roomId: { type: mongoose.Schema.Types.ObjectId, ref: 'rooms' },
senderId: { type: mongoose.Schema.Types.ObjectId, ref: 'users' },
text: String,
deliveredTo: [{ user: { type: mongoose.Schema.Types.ObjectId, ref: 'users' }, timestamp: Date }],
readBy: [{ user: { type: mongoose.Schema.Types.ObjectId, ref: 'users' }, timestamp: Date }],
})
app.js
Mongo.connect(uri, (err, client) => {
mongoose.connect(uri)
socketClient.on('connection', socket => {
//
//on load all users for login select
User.find().then(users => socket.emit('users', users))
....
//
//setting pipeline for readStream to only watch for changes in the messages collection
//where the roomId field is the current room the user has loaded
//setting fulldocument : 'updateLookup' option will always return the fulldocuemnt with any updates
//without this, the fulldocument will only be returned on our insert change
const pipeline = [{ $match: { 'fullDocument.roomId': ObjectId(room.id) } }]
const collection = client.db('chat').collection('messages')
const messageStream = collection.watch(pipeline, { fullDocument: 'updateLookup' })
messageStream.on('change', async function (change) {
const updates = change.updateDescription?.updatedFields
//
//if a new document is created in messages, then push that message to the client
if (change.operationType.match(/insert/i)) {
const doc = await Message.findById(change.fullDocument._id).populate(
'deliveredTo.user readBy.user'
)
const status = await getStatus(doc, room.id)
socket.emit('push message', { doc, status: status })
//
//if delivered to has been updated on a message then send to client
} else if (updates?.deliveredTo) {
const status = await getStatus(change.fullDocument, room.id)
socket.emit('status change', {
id: change.fullDocument._id,
status: status,
user: change.fullDocument.senderId,
})
} else if (updates?.readBy) {
//
//if readby has been updated on a message then send to client
const status = await getStatus(change.fullDocument, room.id)
socket.emit('status change', {
id: change.fullDocument._id,
status: status,
user: change.fullDocument.senderId,
})
}
})
...
})
})
Their Guru's
I'm doin a research on a code I saw on the internet, but if I see the syntax, they have used Hapi/Joi which is depricated.
My question is how can I use this syntax in Joi?
app.post('/test', (req, res, next) => {
const id = Math.ceil(Math.random() * 9999999);
Joi.validate(data, schema, (err, value) => {
if (err) {
res.status(400).json({
status: 'error',
message: 'Invalid request data',
data: data
});
} else {
res.status(200).json({
status: 'success',
message: 'User created successfully',
data: Object.assign({id}, value)
});
}
});
});
By inspecting the types (index.ds.ts) file in Github indeed there is no callback style validate function, only those two methods:
/**
* Validates a value using the schema and options.
*/
validate(value: any, options?: ValidationOptions): ValidationResult;
/**
* Validates a value using the schema and options.
*/
validateAsync(value: any, options?: AsyncValidationOptions): Promise<any>;
However you can use util.callbackify - callbackify examples to transform validateAsync into a callback node style if you really want to:
const Joi = require('joi');
const { callbackify } = require('util');
// A very simple data object
const obj = { a: 23 };
// A very simple joi schema which checks if schema is an object with the `a` property being a string
const schema = Joi.object({
a: Joi.string(),
});
// Callback style (which i don't recommend since its not available in `joi` package)
callbackify(() => schema.validateAsync(obj))((err, ret) => {
if (err) {
return res.status(400).json({
status: 'error',
message: 'Invalid request data',
data: data,
});
}
res.status(200).json({
status: 'success',
message: 'User created successfully',
data: Object.assign({ id }, value),
});
});
I would use what Joi provide, as in the following 2 examples:
// using async/await - you need to mark you controller as async, like:
// app.post('/test', async (req, res, next) => ...
try {
await schema.validateAsync(obj);
res.status(200).json({
status: 'success',
message: 'User created successfully',
data: Object.assign({ id }, value),
});
} catch (err) {
res.status(400).json({
status: 'error',
message: 'Invalid request data',
data: data,
});
}
Or even with just promises:
schema
.validateAsync(obj)
.then(() =>
res.status(200).json({
status: 'success',
message: 'User created successfully',
data: Object.assign({ id }, value),
})
)
.catch(() =>
res.status(400).json({
status: 'error',
message: 'Invalid request data',
data: data,
})
);
How can I get the object back from the database in Express js? When I do a POST request I only get a status 201 back and not the object from the response.
The way as it is below it returns an empty res.data field instead of the object.
router.post('/', async (req, res) => {
const websites = await loadWebsitesCollection();
await websites.insertOne({
title: req.body.title,
url: req.body.url,
cms: req.body.cms,
fw: req.body.fw,
user: req.body.user,
createdAt: new Date()
});
//TODO Need to get the response from the post request
res.status(201).send();
res.status(404).send('Sorry, we cannot find that!');
res.status(500).send({ error: 'something blew up' });
})
To get all the objects back in an array I can do so like this:
res.send(await websites.find({}).toArray());
In mongoDB insertOne method returns a document containing acknowledged as true and the currently inserted id (ObjectId) as insertedId. So you can store the response from mongoDB in a variable and if there's any insertedId found you can query the data from mongoDB or prepare your data from request body.
...
const insertion = await websites.insertOne({
title: req.body.title,
url: req.body.url,
cms: req.body.cms,
fw: req.body.fw,
user: req.body.user,
createdAt: new Date()
});
let data = {};
if (insertion.acknowledged) {
// ... prepare the data
data = await websites.findOne({_id: insertion.insertedId});
}
...
res.send(data);
I hope it works!
ok you can try this way
try{let websites = await loadWebsitesCollection.insertOne({
title: req.body.title,
url: req.body.url,
cms: req.body.cms,
fw: req.body.fw,
user: req.body.user,
createdAt: new Date() });;
res.send(websites );}catch(e){res.status(400).send(e)}
or this way
try{var websites = new loadWebsitesCollection({
title: req.body.title,
url: req.body.url,
cms: req.body.cms,
fw: req.body.fw,
user: req.body.user,
createdAt: new Date()})var reswebsites = await websites .insertOne(); res.send(reswebsites );}catch(e){res.status(400).send(e)}
I am creating a RESTful API using Node.js and mongoose by following the tutorial by Acedemind. I have got it working just fine and am now expanding it to allow the client to post several products in the same order. Basically I am modifying a simple "POST" request to be an array instead of some variables. My problem is that I run into a long validation error that hinders the array from being created. Here is the code for the post request:
router.post("/", async (req, res, next) => {
const productsMaybeFalses = await Promise.all(req.body.products.map(async ({ productId })=> {
const product = await Product.findById(productId);
if (!product) {
return false;
}
return {
...product,
productId,
}
}));
const errors = productsMaybeFalses
.map((productMaybeFalse, index) => {
return {
productMaybeFalse, index
}
})
.filter(({ productMaybeFalse }) => !productMaybeFalse)
if (errors.length) {
console.log(error);
return;
}
console.log(productsMaybeFalses);
const products = productsMaybeFalses
.filter((productMaybeFalse) => productMaybeFalse);
const order = new Order({
_id: mongoose.Types.ObjectId(),
products: products
});
return order.save().then(results => {
console.log(results);
res.status(201).json(results.map((result) => ({
message: "order stored",
createdOrder: {
_id: result._id
},
request: {
type: "GET",
url: "http://localhost:3000/orders/" + result._id
}
})));
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
})
})
})
And here is the Schema for the Order:
const mongoose = require("mongoose");
const pSchema = mongoose.Schema({
productId: { type: mongoose.Schema.Types.ObjectId, ref: "Product", required: true},
quantity: { type: Number, default: 1}
});
const orderSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
products: [pSchema]
});
module.exports = mongoose.model("Order", orderSchema)
To keep this question from being too long I will only post the end part of the error. The part that I feel tells the most information abut the problem. If anyone wants the whole error message to better understand the problem and maybe come up with a solution for me I will be very willing to post it as well. Here it is:
kind: 'Array',
value: [Array],
path: 'products',
reason: TypeError: value[i].toObject is not a function
at DocumentArray.cast (/Users/axelhagman/Documents/Jacobs/node_modules/mongoose/lib/schema/documentarray.js:309:27)
at DocumentArray.SchemaType.applySetters (/Users/axelhagman/Documents/Jacobs/node_modules/mongoose/lib/schematype.js:755:12)
at model.$set (/Users/axelhagman/Documents/Jacobs/node_modules/mongoose/lib/document.js:922:18)
at model._handleIndex (/Users/axelhagman/Documents/Jacobs/node_modules/mongoose/lib/document.js:740:14)
at model.$set (/Users/axelhagman/Documents/Jacobs/node_modules/mongoose/lib/document.js:697:22)
at model.Document (/Users/axelhagman/Documents/Jacobs/node_modules/mongoose/lib/document.js:114:12)
at model.Model (/Users/axelhagman/Documents/Jacobs/node_modules/mongoose/lib/model.js:73:12)
at new model (/Users/axelhagman/Documents/Jacobs/node_modules/mongoose/lib/model.js:4324:13)
at router.post (/Users/axelhagman/Documents/Jacobs/api/routes/orders.js:70:17)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:189:7) } },
_message: 'Order validation failed',
name: 'ValidationError' }
POST /orders/ 500 440.085 ms - 7622
I am very new to using node.js and creating API overall so any help would be very much appreciated. Thanks!