I have a schema problem. I dont get the right schema in my api. here is my api :
var Meetup = require('./models/meetup');
module.exports.create = function (req, res) {
var meetup = new Meetup(req.body);
meetup.save(function (err, result) {
console.log(result);
res.json(result);
});
}
module.exports.list = function (req, res) {
Meetup.find({}, function (err, results) {
res.json(results);
});
}
The console.log displays { __v: 0, _id: 58343483ff23ad0c40895a00 } while it should display { __v: 0, name: 'Text input', _id: 58343076b80874142848f26e }
here is my model:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var Meetup = new Schema({
name: String,
});
module.exports = mongoose.model('Meetup', Meetup);
If req.body is undefined (as you wrote in the comments) then obviously new Meetup(req.body); cannot populate the new objects with any data (like {name: 'Text input'} or anything else) since it is called with undefined as an argument.
Make sure you use the body-parser and that you pass the correct data in your request.
Also, check for errors. Every callback that takes the err argument should be in the form of:
module.exports.list = function (req, res) {
Meetup.find({}, function (err, results) {
if (err) {
// handle error
} else {
// handle success
}
});
}
How to track the problem:
make sure you use the body-parser on the backend
make sure you pass the correct data on the frontend
make sure that the data passed by your frontend is in the correct place (body)
make sure that the data is in the correct format (JSON? URL-encoded?)
add console.log(req.body) after new Meetup(req.body); to know what you save
open the Network tab in the developer console of your browser and see what is transferred
Related
I'm trying to create a REST Service. The route below will execute a stored procedure that will return json results
app.get('/spparam', function (req, res) {
var sql = require("mssql");
// config for your database
var id=0;
var config = {
user: 'username',
password: 'password',
server: 'hostname',
database: 'databasename'
};
// connect to your database
sql.connect(config, function (err) {
if (err) console.log(err);
// create Request object
var request = new sql.Request();
if(!mylib.isEmptyObject(req.query)){
id=req.query.id;
}else if(!mylib.isEmptyObject(req.params)){
id=req.params["id"];
}
// Executing Stored Prcoedure
request.input('requestid', sql.Int, id)
.execute("Request_Get_ById").then(function(recordSet) {
//console.dir(recordsets);
//console.dir(err);
res.send(recordSet);
sql.close();
}).catch(function(err) {
console.log(err);
});
});
});
I want to minimise my code by creating one route that will handle both query (/spparam?id=1) and params (/spparam/:id/). Is this possible? Is there a better way to handle what I need?
Yup, you can do that with Express like this:
app.get('/spparam/:id?', function (req, res) {
const id = req.params.id || req.query.id;
// the rest of your function, and use id without caring about whether
// it came from params or query
// change order if you want to give preference to query
}
The Express.js docs say it uses path-to-regexp for route matching purposes. There you can see this quote:
Parameters can be suffixed with a question mark (?) to make the
parameter optional.
In javascript, the construct var a = b || c assigns the value of b to a if b is not false-y, and otherwise it assigns the value of c to a.
I am making an api rest in which I want to make HTTP requests using Postman, specifically I want to perform a search or update a mongodb document, but this must be by an id which is not the doc_id that provides mongo
models Schema
'use strict'
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const infoClientSchema = Schema ({
idusr: String, /*this is require*/
name: String,
phone: Number,
address: String,
riff: String,
state: String,
city: String,
email: {type: String}
})
module.exports = mongoose.model('InfoCli',infoClientSchema)
Controller (This is the get method I know using findById and is working)
'use strict'
const InfoCli = require('../models/infoclient')
function getInfoCli(req, res){
let infocliId = req.params.infocliId
InfoCli.findById(infocliId, (err, infocli) =>{
if (err) return res.status(500).send({message: 'Error making
request: $(err)'})
if (!infocli) return res.status(404).send({message: 'The client does
not exist '})
res.status(200).send({infoclient: infocli})
})
}
Controller (This is the get method which I thought would work using findOne)
function getInfoByUsr(req, res){
let idusr = req.body.idusr
InfoCli.findOne(idusr, (err, infocli) => {
if (err) return res.status(500).send({message: 'Error making
request: $(err)'})
if (!infocli) return res.status(404).send({message: 'The client does
not exist '})
res.status(200).send({infoclient: infocli})
console.log(infocli) /*The console is not showing anything*/
})
}
Controller (This is the put method which I thought would work using findOneAndUpdate)
function updateByUsr(req, res){
let idusr = req.body.idusr
let update = req.body
InfoCli.findOneAndUpdate(idusr, update, (err, infocliUpdate) => {
if (err) return res.status(500).send({message: 'Error making
request: $(err)'})
if (!idusr) return res.status(404).send({message: 'The client does
not exist '})
res.status(200).send({infocliente: infocliUpdate})
})
}
Routes (not 100% sure about this)
const express = require('express')
const InfoCliCtrl = require('../controllers/infoclient')
const api = express.Router()
api.get('/infoclient/:infocliId', InfoCliCtrl.getInfoCli) /*working*/
api.get('/infoclient/:idusr', InfoCliCtrl.getInfoByUsr)
In your app.js/server.js
you should have bodyparser installed
api.get('/infoclient/:infocliId', InfoCliCtrl.getInfoCli)
api.post('/infoclient/:idusr', InfoCliCtrl.updateByUsr)
If you are passing data as URL parameter, like this /infoclient/:infocliId then you can access that using req.params.infocliId
If you are passing using POST body then you can access data using req.body.
In infoClient.js
To fetch user data
exports.getInfoCli = function(req, res, next){
var incomingData = req.params.infocliId;
InfoCli.findOne({idusr: incomingData}, function(err, data){
if(err){
return res.status(500);
} else {
return res.status(200).send({infoclient: data})
}
});
}
Call the above code by
GET - http://localhost:port/infoclient/3874234634 this 3874234634 is your infocliId you need to pass in route
To update user data
exports.updateByUsr = function(req, res, next){
var userId = req.params.idusr;
var updateData = req.body;
InfoCli.findOneAndUpdate({idusr: userId}, updateData, {new: true }, function(err, data){
if(err){
return res.status(500);
} else {
return res.status(200).send(data)
}
});
}
In the update code we have used {new : true} is to return updated document from DB
Call the above code by
POST method - http://localhost:port/infoclient/3874234634 with data in POST body {name: 'pikachu', phone: 12345, ...}
so you read the userid in url parameter using req.params and body data in req.body
I think you simply need to change the line let idusr = req.body.idusr in your getInfoByUsr() function to let idusr = req.params.idusr
http://expressjs.com/en/api.html#req.body
http://expressjs.com/en/api.html#req.params
Also check the syntax of your findOne and findOneAndUpdate query (because idusr is not a Mongo _id but sort of custom String id):
InfoCli.findOne({ idusr: idusr }, (err, infocli) => { ...
InfoCli.findOneAndUpdate({ idusr: idusr }, update, (err, infocliUpdate) => {..
http://mongoosejs.com/docs/api.html#model_Model.findOne
Thank you all, your answers help me to correct many things in the code.
The problem was a horrible mistake in the routes
See how I was using the same path for two requests
api.get('/infoclient/:infocliId', InfoCliCtrl.getInfoCli) /*working*/
api.get('/infoclient/:idusr', InfoCliCtrl.getInfoByUsr)
The problem was that when I used the identifier for idusr it was in conflict with the ObjectId search
Now
api.get('/infoclient/idusr/:idusr', InfoCliCtrl.getInfoByUsr)
I'm learning Angular by following this tutorial. No what I don't get is why is there two ways of saving/editing an object? They don't really explain it.
first way (in the index.js router):
router.post('/posts', function(req, res, next) {
var post = new Post(req.body);
post.save(function(err, post) {
if (err) { return next(err); }
res.json(post);
});
});
second way (in the mongoose model):
var mongoose = require('mongoose');
var PostSchema = new mongoose.Schema({
title: String,
link: String,
upvotes: { type: Number, default: 0 },
comments: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Comment' }]
});
PostSchema.methods.upvote = function(cb) {
this.upvotes += 1;
this.save(cb);
};
mongoose.model('Post', PostSchema);
The two ways are actually the same.
In the first example a new instance of Post is created. The save is called on this instance because it exists on PostSchema (inherited from Mongoose Schema).
In the second example we define a new method on PostSchema so when invoked, this refers to the instance of the Post.
The cb parameter is a callback which is normally a function.
Look at this example.
router.post('/posts', function(req, res, next) {
var post = new Post(req.body);
post.save(function(err, post) {
if (err) { return next(err); }
var callback = function(err, post) {
console.log("Upvoted");
res.json(post);
}
post.upvote(callback);
});
});
Here a new post is created with one upvote by default.
A new instance of Post is created and saved in the db. After save, upvote method is called. After the upvote was saved in the db, the new post is returned in the response.
Hope this helps.
The first part is a normal Mongoose save in which you create a new object from the schema. The second is having a method defined in the schema to handle the save.
PostSchema.methods.upvote = function(cb) {
this.upvotes += 1;
this.save(cb);
};
For example, assuming I already have a saved post and I want to upvote it. I can have a route like this:
router.put('/post/:id/upvote', function(req, res, next) {
Post.findOne({_id: req.params.id}, function (error, post) {
post.upvote(function(error, post) {
res.json(post);
});
});
});
In simple terms, the code above makes use of the inbuilt "upvote" method that is already defined in the Schema, so all objects created from the Schema will have an "upvote" method that will increase the number of upvotes. It is the same thing as doing this:
router.put('/post/:id/upvote', function(req, res, next) {
Post.findOne({_id: req.params.id}, function (error, post) {
post.upvotes += 1;
post.save(function(error, post) {
res.json(post);
});
});
});
Just that it is cleaner and saves you a few keystrokes.
I am trying to pass some data from my db to the router which then passes the data to the view.
My model code :
var mysql = require('mysql');
var connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '',
database: 'test'
});
var result; // empty var which should later be filled with the querys result
connection.connect();
var query = connection.query('SELECT * FROM users', function(err, res, fields) {
if (err) throw err;
result = res; // overwrite result with the querys result
console.log(res); // This prints out everything I need
});
module.exports = {
data: result // should contain the query result (= 2 objects in this case)
}
Now to my route file :
var express = require('express');
var router = express.Router();
var Users = require('../models/users');
console.log(Users.data);
/* GET home page. */
router.get('/users', function(req, res) {
res.render('api', { data: Users.data, title: "Test API Output" });
});
module.exports = router;
When I console.log Users or Users.data I get undefined. I don't really get why this is the case. How else am I supposed to pass data along the files.
All help is gladly read :) Thank you.
module.exports are being evaluated the second you require and variables are not passed by reference in this case.
What that means for your code is the following:
var result; // result is "undefined" because it does not contain a value here
// You are doing your DB queries here...
module.exports = {
data: result // ...and because the query has not finished here yet, result
// is still undefined.
// This is also a good example of a so called "race condition", because there is a
// slight (improbable) chance that the query might have already finished.
// Hence, it could happen that sometimes result is already filled.
}
When you now require the above file in another file of your code, the above is being evaluated and saved straight away (result is undefined at that point in time, hence it is also undefined when it exports).
Your query is being executed and written into the result variable, but at that point in time you can not modify the exported variable anymore – because it is it's own variable and not merely a reference to result).
What you could do is the following:
function getData(callback) {
connection.query('SELECT * FROM users', function(err, res, fields) {
callback(err, res);
});
}
module.exports = {
getData: getData
}
and then in your other file:
var Users = require('../models/users');
Users.getData(function(err, result) {
// TODO: Error handling.
console.log(result);
});
That's exactly why it's so easy with JavaScript to end up in callback hell, because of it's asynchronous nature.
The above is the exact same situation as if you, f.e., want to get some data via AJAX from a server and then fill tables with it. When you start creating the table before you have the data (so the AJAX request is not yet complete), you end up with an empty table. What could do is:
you create a variable that holds your data and
a function that creates the table
when you then ask the server for the data (via AJAX) you wait until you get the data (completion callback) and only then you start creating the table: filling your variable and calling the function to fill the table with the data.
Server-Side JavaScript is the same as client-side. Never forget this.
As a little homework for the reader: the way to get out of callback hell is by reading up on promises – a pattern/architecture which reduces indents and saves lots of headaches :)
(update: Lucas' answer is basically telling the same thing as I did)
(update 2: wrong way of handling err)
I suggest realize the consult in the route file, some like this:
var express = require('express');
var router = express.Router();
var Users = require('../models/users');
var mysql = require('mysql');
var connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '',
database: 'test'
});
var result; // empty var which should later be filled with the querys result
connection.connect();
/* GET home page. */
router.get('/users', function(req, res) {
var query = connection.query('SELECT * FROM users', function(err, res, fields) {
if (err) throw err;
result = res; // overwrite result with the querys result
res.render('api', { data: res.data, title: "Test API Output" });
});
});
module.exports = router;
But you can configure the connection with database in another file, in libs/mysql_connect.js.
The undefined is caused because the response of connection.query don't works out of the connection.query.
If you really want the query to run only once and then just re-use the already queried data, I think you are after something like this for your model:
...
var data;
var mymodel = {};
...
mymodel.getData = function(callback) {
if(data) {
callback(data);
} else {
db.query('select * from users', function(err,res,fields) {
// error checking and such
data = res;
callback(data);
});
}
}
module.exports = mymodel
In your router, you'd then use it like this:
...
router.get('/users', function(req, res) {
Users.getData(function(mydata) {
res.render('api', { data: mydata, title: "Test API Output" });
});
});
The first time you call getData, you'll get a fresh result, and on subsequent calls you get the cached result.
While you could expose data in mymodel directly, and only use the callback in case it is still undefined, that'd make your code in the router more convulated.
I am new to AngularJS and I was trying to update my MongoDB Database. I am having an issue when I am trying to update an object inside my collection. The following is my attempt at trying to do so:
//listviewFactory is already injected and returns an event object
//I call $scope.put when clicking (ng-click) on a button
$scope.event = listviewFactory.getEvent();
$scope.put = function(event){
var currentUser = {};
if($cookieStore.get('token')) {
currentUser = User.get();
}
event.attendees.push(currentUser);
$http.post('/api/events/' + event._id, event).success(function(data){
for(var i = 0; i < event.attendees.length; i++){
console.log("Attendees: ", event.attendees[i]);
}
$location.path('/');
});
};
I'm just unsure of why my code isn't working. When I do the $http put request, my function is successful and proceeds to perform the console.log. When I print out the attendees array, I saw that the currentUser object is indeed appended to my event.attendees array. However, when I check my database using the Mongo shell, My attendees array is not updated and remains blank. Any ideas on why or where I may be wrong at? Below is also my server side code in how I am routing my application and specifying how to store the information in MongoDB.
module.exports = function(app) {
app.use('/api/events', require('./api/event'));
...
}
module.exports = mongoose.model('Event', EventSchema);
var express = require('express');
var controller = require('./event.controller');
var router = express.Router();
router.get('/', controller.index);
router.put('/:id', controller.update);
// Updates an existing event in the DB.
exports.update = function(req, res) {
if(req.body._id) { delete req.body._id; }
Event.findById(req.params.id, function (err, event) {
if (err) { return handleError(err); }
if(!event) { return res.send(404); }
var updated = _.merge(event, req.body);
updated.save(function (err) {
if (err) { return handleError(err); }
return res.json(200, event);
});
});
};
EDIT: When I console.log currentUser:
Current User:
Resource {$promise: Object, $resolved: false, $get: function, $save: function, $query: function…}
$promise: Object
$resolved: true
__v: 0
_id: "53dd72fb5a24aa781a3cbde8"
email: "test#test.com"
name: "Test User"
provider: "local"
role: "user"
EDIT: Mongoose schema:
'use strict';
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var EventSchema = new Schema({
startDate: Date,
endDate: Date,
eventLocation: String,
eventName: String,
attendees: Array
});
I assume you are using mongoose. Try
updated.markModified('attendees');
before
updated.save call.
Regarding pushing the currentUser, do something equivalent to
event.attendees.push({__v: 0
_id: "53dd72fb5a24aa781a3cbde8"
email: "test#test.com"
name: "Test User"
provider: "local"
role: "user"})
i.e just push the relevant fields.