Completely stuck. (MongoDB / MERN issue) - node.js

Im creating an app, and right now I'm working on a feature that takes in user input, and stores it in the database. I've done this plenty of times on other projects, but have come to a problem that I just can't solve.
So far, when a user types in their info and hit's enter, the data is sent to the back end, and start's to head towards the endpoint. I have a controller set up with my 'create' function. It is the following:
const db = require('../database/models');
module.exports = {
// create: function(req, res) {
// console.log('hit controller');
// db.Listing
// .create(req.body)
// .then(dbModel => res.json(dbModel))
// .catch(err => res.status(422).json(err));
// }
create: function(req, res) {
console.log('!!!!HERE:CONTROLLERS' + req.body.receiver);
db.Listing
.create(req.body)
.then(function(dbModel) {
console.log(dbMessage);
res.json(dbModel)
})
.catch(function(err) {
res.json(err);
});
}
};
I have two functions because I tried something a little different for each with the same result. Once it gets to this point, I get the following error ('hit controller' is just the console.log in the create function):
'hit controller'
TypeError: Cannot read property 'create' of undefined
I also get the following error in my console on the browser: xhr.js:178 POST http://localhost:3000/listing/ 500 (Internal Server Error) err from saveListing Error: Request failed with status code 500 at createError (createError.js:16) at settle (settle.js:18) at XMLHttpRequest.handleLoad (xhr.js:77)
Which is coming from my addListing page:
handleFormSubmit = (event) => {
event.preventDefault();
console.log('hit');
console.log("state in handle submit", this.state.title)
if (
this.state.title &&
this.state.description &&
this.state.duration &&
this.state.datesAvailable &&
this.state.tags
) {
API.saveListing({
title: this.state.title,
description: this.state.description,
duration: this.state.duration,
datesAvailable: this.state.datesAvailable,
tags: this.state.tags
})
.then(res => console.log('success'))
.catch((err) => console.log("err from saveListing", err));
}
};
Here is my models file:
var mongoose = require("mongoose");
var Schema = mongoose.Schema;
var ListingSchema = new Schema({
// `title` is of type String
title: {
type: String,
required: true
},
description: {
type: String,
required: true
},
duration: {
type: String,
required: true
},
datesAvailable: {
type: String,
required: true
},
hashtags: {
type: Array,
required: true
}
});
var Listing = mongoose.model("Listing", ListingSchema);
module.exports = Listing;
So re-cap: When the user hit's the submit button, the form runs a function, hit's the API with the route it needs, heads to the sever, then to the route for the post request, then the llistingController for the create function for the post, while pulling from my Listing DB model. Once the create function is reached, it poops out. The data is apparently undefined, but I don't know why and can't figure it out.
I feel like what I'm running into is probably basic as fuck but I can't for the life of me figure it out. Additionally if anyone knows some bad ass resources for adding to my mongo/express/react knowledge i'd love to see it.

You're importing db from the models file, but that file is exporting Listing. Import Listing and use Listing.create instead of db.Listing.create.

As 223seneca said, I changed db.Listing.create in my controller to just Listing.create. Then I required Listing in the controller file to the exact file, instead of to just the models folder('const Listing = require('../database/models/Listing');' instead of 'const Listing = require('../database/models');'.
That was all I needed!

Related

Deleting object from mongoose database not working if site is not refreshed

app.delete("/api/persons/:id", (req, res, next) => {
Person.findByIdAndDelete(req.params.id)
.then((result) => {
res.status(204).end();
})
.catch((error) => next(error));
});
Not sure how to even explain this properly, but there is my delete method. It works fine for objects that are allready in the databases, but if I add a new one and I dont refresh the site, I get error: Cast to ObjectId failed for value "undefined" (type string) at path "_id" for model "Person"
Below is my mongoose schema if that helps:
const personSchema = new mongoose.Schema({
name: { type: String, required: true },
number: { type: Number, required: true },
});
personSchema.set("toJSON", {
transform: (document, returnedObject) => {
returnedObject.id = returnedObject._id.toString();
delete returnedObject._id;
delete returnedObject.__v;
},
});
My guess is you're optimistically updating your frontend with the new Person without waiting for a successful DB response with the new data. That is a valid technique, but gets you into trouble if you're not careful.
My suggestion would be to also send the new value from the database back to your app right away say it can stay in sync. You likely have no _id value on the front end if you're optimistically updating the app before a DB response.
Something like this:
app.post("api/person/new", async (req, res) => {
try {
const person = new Person(req.body)
await person.save()
res.status(201).send({ person })
} catch (err) {
res.status(500).send({ err })
}
})
And then more importantly, your API handler on the frontend needs to take that returned person value and use it to update the values on your front end, so it has access to the _id property for immediate deletion. And if there's an error creating the person for any reason, you can remove the person from the front end, or handle it however you wish.
I don't know what your app is built with, so I can write a sample bit of code for it.

Axios react node post request is giving a 422 (Unprocessable Entity)

I am trying to save the desired book to my MongoDB database when I press the saved button I get a 422 error I pass in the data as an object but for some reason, the data doesn't appear in the response back from the server The data is being passed to the Axios call but for some reason, the data property always returns an empty object,
The save handler
const handleSave = (event) => {
event.preventDefault();
let save = books.filter((book) => book.id === event.target.id);
// console.log(save);
// const da/ta = ;
// console.log(data);
API.saveBook({
title: save[0].title,
authors: save[0].author,
description: save[0].description,
image: save[0].image,
})
.then((res) => {
alert("book saved");
console.log(res);
// console.log(data);
})
.catch((err) => {
// console.log(data);
console.log("book not saved");
console.log(err.response);
});
};
This is the book model and the heroku link where you can see what is being logged out
const bookSchema = new Schema({
title: { type: String, required: true },
authors: [{ type: String, required: true }],
description: { type: String, required: true },
image: { type: String },
date: { type: Date, default: Date.now },
});
Heroku Link
github
I have console.logs in my inspect so you can check those out to see the response im getting back
I have cloned this repository and tested on both your Heroku link and locally, and cannot recreate the error locally. I suspect something to do with the MongoDB server rather than a code issue. I recommend you test creating a record in the live/Heroku-attached MongoDB server using an alternative method.
Thanks,
Will Walsh
Looks like volumeInfo.description is undefined for some books. The API returns a 422 error since description is required but is not present in the request payload. You could pass a default description if the book doesn't have one.
result = {
// ...
title: result.volumeInfo.title,
description: result.volumeInfo.description || "This book doesn't have a description",
// ...
}
Or you could remove the required validation for the description field if it's not an issue.
I would recommend you rename author to authors in the result object for clarity.
result = {
// ...
authors: result.volumeInfo.authors,
// ...
}

Mongoose .save() returns empty error object, does not save in DB

I am trying to learn MongoDB and typescript but currently running into some issues when trying to create my first document.
When I make a post request from postman, I get "Sending request" for 5 seconds, then it times out and returns an empty error object:
{
"message": {}
}
and the posted data is not saved in my mongoDB.
I first set up connection like this in my server:
mongoose.connect(<string> process.env.DB_CONNECTION, { useNewUrlParser: true}, () =>
console.log("connected to DB!")
);
and get back the correct statement logged, so I am connected.
I have a model that looks like this:
import { model, Schema, Model, Document } from "mongoose";
export interface IUser extends Document {
name: string,
}
const UserSchema: Schema = new Schema(
{
name: {
type: Schema.Types.String,
required: true,
},
},
<any> {timeStamps: true}
);
const User: Model<IUser> = model<IUser>('User', UserSchema);
export default User;
The problem could be in there, but I don’t think it is.
Then, here is my post request for the endpoint I call:
router.post('/add', async (req: Request, res: Response): Promise<any> => {
try {
const user: IUser = new User({
name: req.body.name
});
const savedUser: IUser = await user.save();
res.status(200).json(savedUser);
} catch (err: any) {
res.status(500).json({message: err});
console.log("There was an error");
}
})
Here is where I believe the error is becuase every time the request gets stuck on awaiting the .save()
Any help would be great! Thanks!
The issue was with the initial database connection, the password contained characters that that weren't being encoded. One solution to encode special characters is to make use of the built-in encodeURIComponent() function.

How to get another colletion data with mongoose populate

I have the following models in node js and i want to get data from file schema and from client schema in just one call, i was reading about populate but have no ideia how to use that.
This is my model
const mongoose = require('mongoose');
const fileSchema = mongoose.Schema({
_id: mongoose.SchemaTypes.ObjectId,
client_id: mongoose.SchemaTypes.ObjectId,
user_id: mongoose.SchemaTypes.ObjectId,
status: String,
name: String,
path: String,
clients: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Client' }]
});
const clientSchema = mongoose.Schema({
_id: mongoose.SchemaTypes.ObjectId,
name: String,
img: String
});
module.exports =
mongoose.model('File', fileSchema, 'files'),
Client = mongoose.model('Client', clientSchema, 'clientes');
This is how i am getting the file data now
exports.getFiles = (req, res, next) => {
File.find({ field: res.locals.field })
.select('_id client_id user_id status name path')
.exec()
.then(file => {
res.status(200).json({
response: file
});
})
.catch(err => {
console.log(err);
res.status('500').json({
error: err
});
});
};
this returns an json response, when i tried to use populate i got an empty array.
You're almost there but you have an issue with your find search. At least with the File model you posted, you don't have a field called 'field' so you won't get any results.
Let's pretend that you're trying to find a file based off of its name and the request is being sent to the url 'blah/files/:name' and it looks like you're using Express.js so this should work.
To use populate, you usually do something like:
File.find({ name: req.params.name })
.populate('clients')
.exec()
.then(files => {
res.status(200).json({
response: files
});
})
.catch(err => {
console.log(err);
res.status('500').json({
error: err
});
});
What you have in your 'select' bit it not necessary since you're starting the search based on the File model and you're just asking it to return all of the fields you have anyway on that model. You get those returned in the result 'for free'.
The populate is flagged out on the 'clients' field since you specified in the File model that it's an object id that references the Client model. Mongoose should handle it basically automagically. However, be careful, ALL of the fields on the Client model will be populated in the clients array of the File. If you want to return only one or a couple fields for your clients, it's there that you should use the select.
Also a note: the find method will return an array even if it's just a result of one document. If you are expecting or wanting just one result, use the findOne method instead.
Update
It looks like there's also a bugaboo in your module exports in the model file, which could be why you are having problems. My coding style is different from yours but here's how I would do it just to be sure that there are no mess ups :
const File = mongoose.model('File', fileSchema);
const Client = mongoose.model('Client', clientSchema);
module.exports = { File, Client };
Then in your router code, you import them as so:
const { File, Client } = require('<path-to-model-file>');

Node, Mongoose, Redux: GET request failure

I am trying to make the following get request, which is intended to simply return documents from a collection. Once I get the data, it should flow through a reducer into the redux state object. But the get request fails.
var QuestionModel = require('./src/models/fv-questionModel')
mongoose.connect('mongodb://localhost/FVDB')
var app = express();
app.use(compression())
app.use(express.static(path.join(__dirname, 'public')));
app.set('port', process.env.PORT || 8080);
app.get('/api/recent', (request, response) => {
if (error) {
console.log(error)
}
// Get documents and perform callback on recentQuestions data
QuestionModel.find((error, recentQuestions) => {
if (error) {
response.send('unable to retrieve data')
} else {
response.json(recentQuestions)
}
})
})
this request was working until I changed the structure of the data in the collection. Here is my current mongoose schema:
const questionSchema = new mongoose.Schema({
title: String,
options: [{ oid: Number, choice: { name: String, count: Number } }],
qid: Number,
})
const QuestionModel = mongoose.model('Question', questionSchema, 'questionBank')
module.exports = QuestionModel
The previous schema:
const questionSchema = new mongoose.Schema({
title: String,
options: Array,
qid: Number,
})
Here is the Redux action that makes the GET request:
export function recentQuestions() {
let recentQs = axios.get('/api/recent')
return {
type: 'GET_RECENT',
payload: recentQs,
}
}
And the reducer to handle that data:
export default function(state = null, action) {
switch (action.type) {
case 'GET_RECENT':
return action.payload.data
}
return state
}
But when the GET request is made, it returns a '404: Not Found' error. When I navigate my browser to localhost:8080/api/recent I get the message 'cannot GET /api/recent'. The express server itself is working.
I cannot for the life of me figure out why this request is no longer working. I know there are other questions about failed GET requests but none seem to pertain to this issue. Any help would be greatly appreciated.

Resources