I am currently developing a RESTful API with Express using TypeScript and MongoDB. My ORM of choice is mongoose. When test my Application with the following POST request:
curl -i -X POST -H "Content-Type: application/json" -d '{ "first_name":"hansi"}' localhost:3000
I get the following error message, stating that the attribute "first_name" is required:
{ ValidationError: Person validation failed: first_name: Enter a first name
at ValidationError.inspect (/Users/schnabl/s6/node_modules/mongoose/lib/error/validation.js:59:24)
at formatValue (internal/util/inspect.js:453:31)
at inspect (internal/util/inspect.js:193:10)
at Object.formatWithOptions (util.js:85:12)
at Console.(anonymous function) (console.js:188:15)
at Console.log (console.js:199:31)
at /Users/schnabl/s6/src/routes/person.js:23:29
at /Users/schnabl/s6/node_modules/mongoose/lib/model.js:4533:16
at parallel (/Users/schnabl/s6/node_modules/mongoose/lib/model.js:2667:16)
at /Users/schnabl/s6/node_modules/async/internal/parallel.js:39:9
at /Users/schnabl/s6/node_modules/async/internal/once.js:12:16
at iteratorCallback (/Users/schnabl/s6/node_modules/async/eachOf.js:60:13)
at /Users/schnabl/s6/node_modules/async/internal/onlyOnce.js:12:16
at /Users/schnabl/s6/node_modules/async/internal/parallel.js:36:13
at callbackWrapper (/Users/schnabl/s6/node_modules/mongoose/lib/model.js:2640:20)
at /Users/schnabl/s6/node_modules/mongoose/lib/model.js:4533:16
errors:
{ first_name:
{ ValidatorError: Enter a first name
at new ValidatorError (/Users/schnabl/s6/node_modules/mongoose/lib/error/validator.js:29:11)
at validate (/Users/schnabl/s6/node_modules/mongoose/lib/schematype.js:871:13)
at /Users/schnabl/s6/node_modules/mongoose/lib/schematype.js:924:11
at Array.forEach (<anonymous>)
at SchemaString.SchemaType.doValidate (/Users/schnabl/s6/node_modules/mongoose/lib/schematype.js:880:19)
at /Users/schnabl/s6/node_modules/mongoose/lib/document.js:1913:9
at process._tickCallback (internal/process/next_tick.js:61:11)
message: 'Enter a first name',
name: 'ValidatorError',
properties: [Object],
kind: 'required',
path: 'first_name',
value: undefined,
reason: undefined,
[Symbol(mongoose:validatorError)]: true } },
_message: 'Person validation failed',
name: 'ValidationError' }
However, when I comment the '"required: "enter a string"' out, no server error is thrown. But still the first_name and all the other string attributes I use in my schema aren't added to the collection. There are just the Mongo generated "ids" and default values for the birth_date (current_timestamp) in the collection.
My schema (personSchema.ts):
//imports
const Schema = mongoose.Schema;
export const PersonSchema = new Schema({
first_name: {
type: String
//required: "Enter a first name"
},
...
birth_date: {
type: Date,
default: Date.now
}
}, { collection: 'person' });
The router (personRouter.ts):
//imports
export class PersonRoute {
public personController: PersonController = new PersonController();
public routes(app): void {
...
//getrequests
...
app.route("/").post((req: Request, res: Response) => {
const PersonModel = mongoose.model('Person', PersonSchema);
PersonModel.create(req.body, function (err, post) {
if (err) console.log(err);
res.json(post);
});
});
}
}
Is it possible that there is something wrong with my test request?
EDIT:
I tested the post request in Postman and still getting the same result:
Related
I'm setting up a simple blog form with title, content, and json base64 image data to pass to a local MongoDB server. This is my post route:
router.post('/:userName/posts/new', isAuthenticated, async (req, res) => {
let parsedImage = req.body.file != null && req.body.file != '' ? JSON.parse(req.body.file).data : ''
const newPost = new Post({
title: req.body.title,
content: req.body.content,
imageJson: parsedImage
})
try {
await newPost.save()
res.redirect("/account")
} catch (e) {
console.log(e)
res.redirect("/home")
}
})
And this is my schema model:
const mongoose = require('mongoose');
const postSchema = new mongoose.Schema({
title: {
type: String,
required: true,
},
content: {
type: String,
required: true,
},
imageJson: {
type: String
}
});
module.exports = mongoose.model('Post', postSchema)
no unique: true as you can see. The first time I tried running this, it ran perfectly, redirecting directly to the /account page, as intended in the try catch block. But, on a second attempt, using a different image, different content, and different title, it threw this error:
MongoError: E11000 duplicate key error collection: fullstacksus.posts index: userName_1 dup key: { userName: null }
I have no idea what's going on since userName is a database name used in my user account route, but not in my post-making route. Why is it showing up again? Thanks for your answers.
In your MongoDB there is a unique index on {userName: 1}. Since your schema does not have a userName field, it is missing, i.e. null, in every document, so the index rejects it.
Use the mongo shell to connect and drop the index.
I'm developing ecommerce, for this I'm using MERN, and when the user places an order, the order model takes the id on the user and through this id I can get the user's name and show it on my frontend. so far everything is working perfectly. but when I delete a user who has already placed an order and I try to view the orders. I'm getting this error:
Unhandled Rejection (TypeError): Cannot read property 'name' of null.
because the user with this id was not found.
is there any way to resolve this or not receive this error?
//Node.js
const OrderSchema = new mongoose.Schema(
{
products: [CartItemSchema],
transaction_id: {},
amount: { type: Number },
address: String,
status: {
type: String,
default: "Não está em andamento",
enum: ["Não está em andamento", "Em andamento", "Já enviado", "Concluido", "Cancelado"] // enum means string objects
},
updated: Date,
//this line makes reference to the model user
user: { type: ObjectId, ref: "User" }
},
{ timestamps: true }
);
//React
export const listOrders = (userId, token) =>{
return fetch(`${API}/order/list/${userId}`, {
method: "GET",
headers: {
Accept: 'application/json',
Authorization: `Bearer ${token}`
},
})
.then(response => {
return response.json();
})
.catch(err => console.log(err));
};
As the other answer said, you need to nullcheck every object along the chain, alternatively, you can use optional chaining to avoid cumbersome code like so:
return (
<td>{o?.user?.name` || 'some name`}</td>
)
You have to nullcheck the user object also:
{ (o.user && o.user.name) ? o.user.name : 'abc' }
Is there a way to have the stacktrace show the line in code which called save?
I was testing my validation logic and noticed that Mongoose doesn't print a stacktrace to where I call save(). While the validation does say what went wrong, it is not saying where this is located.
const mySchema = new mongoose.Schema({
name: {
type: String,
required: true,
},
accessToken: {
type: String,
required: true,
},
})
mySchema.statics.createOrUpdate = async function(name, accessToken) {
const animal = await this.findOne({ name })
if (!animal) {
animal = new Animal({ name }) // accessToken is missing and required
}
await animal.save() // expected stacktrace error here
}
ValidationError: Animal validation failed: accessToken: Path `accessToken` is required.
at model.Document.invalidate (/Users/michael/repos/MyApp/node_modules/mongoose/lib/document.js:2622:32)
at /Users/michael/repos/MyApp/node_modules/mongoose/lib/document.js:2442:17
at /Users/michael/repos/MyApp/node_modules/mongoose/lib/schematype.js:1225:9
at processTicksAndRejections (internal/process/task_queues.js:79:11)
If I rethrow the error, I can get a more descriptive stacktrace. But I rather not need to do this:
...
await animal.save().catch((e) => { throw Error(e) })
Error: ValidationError: accessToken: Path `accessToken` is required.
at /Users/michael/repos/MyApp/models/Animal.js:19:42
at processTicksAndRejections (internal/process/task_queues.js:97:5)
I have a nested object which I am able to fetch properly but unable to store the the nested object values in the schema. Below are my code snippets:
header.js Router File
router.post('',(req, res)=>{
//console.log(req.body)
const header = new headerModel({
register:{
name: req.body.name,
url: req.body.url
},
logo:{
image: req.body.image,
altText: req.body.altText,
url: req.body.url
}
})
console.log(header)
})
module.exports = router
Header.js Schema File
const mongoose = require('mongoose')
const headerSchema = mongoose.Schema({
register: {
name: {
type: String
},
url: {
type: String
}
},
logo: {
image: {
type: String,
},
altText: {
type: String
},
url: {
type: String
},
}
})
module.exports = mongoose.model('header', headerSchema)
JSON
{
"register":{
"name":"Register",
"url":"/register"
},
"logo":{
"image":"/imagePath/ab.png",
"alttext":"Home",
"url":"/"
}
}
I need to store the value of name and url in register and signin objects respectively in Router File
the header in Router File when logged on console doesn't include register or logo
Because you get the data from JSON in the wrong way and you haven't saved the header yet. You can solve it by:
let header = new headerModel({
register:{
name: req.body.register.name,
url: req.body.register.url
},
logo:{
image: req.body.logo.image,
altText: req.body.logo.altText,
url: req.body.logo.url
}
})
header.save(function (err, doc) {
// Do some thing you want
})
Related information can be found here.
I'm stuck with the 400 bad request when trying to POST to my mongo db, not knowing what's wrong with the code.
This is the structure in Mongoose:
var exp = mongoose.model('Exp', {
_creator: {
type: mongoose.Schema.Types.ObjectId,
required: true
},
exps: [{
description: {
type: String,
required: true
},
skillId: {
type: mongoose.Schema.Types.ObjectId,
required: true
}
}]
});
This is my test case structure and it is how I want the data to be stored:
const exp = {
_creator: UserOneId, // an ObjectID
exps:[{
description: "Ate an apple",
skillId: SkillOneId // an ObjectID
},{
description: "Took a shower",
skillId: SkillTwoId // an ObjectID
}],
};
The exps part should be an array to allow storing multiple exps, each with a description and a skill id.
Below is my POST function:
app.post('/exps', authenticate, (req, res) => {
var exp = new Exp({
_creator: req.user._id, // got from suthenticate middleware
exps: req.body.exps
});
exp.save().then(() => {
res.send(exp);
}, (e) => {
res.status(400).send(e);
});
})
and my test case:
describe('POST /exps', () => {
it('Should create new exp', (done) => {
request(app)
.post('/exps')
.set('x-auth', users[0].tokens[0].token)
.send(exps)
.expect(200)
.end(done);
})
});
With a structure like this, I just can't figure out what went wrong that's giving me the 400, middleware & variables not mentioned here have passed with other test cases so I don't think it's those.
The error message in test looks like this:
1) POST /exps Should create new exp:
Error: expected 200 "OK", got 400 "Bad Request"
at Test._assertStatus (node_modules/supertest/lib/test.js:250:12)
at Test._assertFunction (node_modules/supertest/lib/test.js:265:11)
at Test.assert (node_modules/supertest/lib/test.js:153:18)
at Server.assert (node_modules/supertest/lib/test.js:131:12)
at emitCloseNT (net.js:1552:8)
at _combinedTickCallback (internal/process/next_tick.js:77:11)
at process._tickCallback (internal/process/next_tick.js:104:9)
Any help appreciated.
Ah, found it. It was a typo in the POST function.