I'm using Mongoose to build a REST API using NodeJs and am running into issues with the params of req.
The code I'm using (model) is as follows:
'use strict';
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var RequestSchema = new Schema({
query: {
type: String,
default: ''
},
number: {
type: String,
default: ''
},
subject: {
type: String,
default: ''
}
});
module.exports = mongoose.model('Cel', RequestSchema)
However, when I use the following code from my controller (answerQuery is used for a POST request) and print out the values I find unexpected values :
exports.answerQuery = function(req, res) {
console.log('query is : ' + req.params.query); // this is undefined
console.log('query is : ' + req.body.query); // this is the value of query
console.log('query is: ' + req.params.number); // this is the value of number
console.log('subject is : ' + req.params.subject); // this is undefined
};
I understand why req.body.query works but am confused as to why req.params.query and req.params.subject don't work (return undefined) but req.params.number does. I haven't used Javascript a lot and think that I might be missing something here.
Edit 0: I'm using a POST request for this
Edit 1: This is my route file:
'use strict';
module.exports = function(app) {
var celRequest = require('../controllers/celSearchController');
// Routes
app.route('/celsearch/:number')
.post(celRequest.answerQuery);
};
Your route is this:
POST /celsearch/:number
This defines one parameter, number. Parameters are accessed through req.params, which is why req.params.number works.
You are trying to access req.params.subject, referring to a parameter called subject, which doesn't exist. Therefore, it's undefined. Same goes for req.params.query.
Because it's a POST route, and it's most common to pass data to POST routes using the request body, that data ends up in req.body. Since the client is passing a parameter called "query" in the request body, req.body.query works.
So:
req.params is used for route parameters, the :NAME placeholders in the route declaration;
req.body is used for parameters passed in the request body, commonly used for POST requests;
req.query is used for parameters passed in the URL as a query string: /foo/bar?query=somevalue. This is generally used for GET requests.
Related
I need a little guidance with routing in my Node/Express app. Initially, I create a new business from the Business model (works fine). After creating the business, I want a separate route which adds the current FX rates offered by that business (these fields will then be updated daily). My business model looks like this (simplified for purpose of example):
let businessSchema = new mongoose.Schema({
name: String,
category: String,
longDescription: String,
images: [ {url: String, public_id: String} ],
usdHkd: { type: String, default: "" },
hkdUsd: { type: String, default: "" },
rateCreatedAt: {
type:Date,
default:Date.now
},
});
When the business is first created, only the name, category, longDesc and images are populated, with default values for the FX rate fields. That works fine using these routes:
/* GET business new /business/new */
router.get("/new", isLoggedIn, asyncErrorHandler(businessNew));
/* POST business create /business */
router.post('/', isLoggedIn, upload.fields([{ name: 'images', maxCount: 10 }]), asyncErrorHandler(businessCreate));
I then set up separate routes/controllers like this for subsequently adding the FX rates, but I don't think these are correctly defined:
/* GET business index /business */
router.get('/:id/remittance/new', asyncErrorHandler(remittanceNew));
/* GET business index /business */
router.put('/:id/remittance', asyncErrorHandler(remittanceCreate));
//Remittances New
async remittanceNew (req, res, next) {
let business = await Business.findById(req.params.id);
res.render('remittanceViews/newRemittance', { business });
},
//Business Update
async remittanceCreate (req, res, next) {
let business = await Business.findByIdAndUpdate(req.params.id, req.body.business);
console.log(business);
//update the post with any new properties
business.usdHkd = req.body.business.usdHkd;
business.hkdUsd = req.body.business.hkdUsd;
business.rateCreatedAt = req.body.business.rateCreatedAt;
//save the updated post in the db
business.save();
//redirect to show page
res.redirect(`/business/${business.id}`);
},
The error message I get when I try to update is:
Cannot read property 'usdHkd' of undefined
Can anyone please advise where I'm going wrong here? Thanks
The error message indicates that usdHkd's parent variable in undefined. Most probably, this error is coming from business.usdHkd in business.usdHkd = req.body.business.usdHkd; (you can confirm it by adding more console.log() lines around this line and checking the outputs).
If business.usdHkd = req.body.business.usdHkd; is giving error, that means, business is undefined. However, you don't need this line as business is already updated by findByIdAndUpdate.
READ: Model.findByIdAndUpdate() and Promises in Mongoose
//Business Update
async remittanceCreate (req, res, next) {
let business = await Business.findByIdAndUpdate(req.params.id, req.body.business);
console.log(business);
// Below code is not required since findByIdAndUpdate() will update your model
/*
//update the post with any new properties
business.usdHkd = req.body.business.usdHkd;
business.hkdUsd = req.body.business.hkdUsd;
business.rateCreatedAt = req.body.business.rateCreatedAt;
//save the updated post in the db
business.save();
*/
//redirect to show page
res.redirect(`/business/${business.id}`);
},
UPDATE
You told that business is defined, but it's not getting updated. The reason is findOneAndUpdate() requires new option to be set as true else findOneAndUpdate() returns the old object (before updating it -- in a sense). So, please change the first line of remittanceCreate() to:
let business = await Business.findByIdAndUpdate(req.params.id, req.body.business, {new: true});
I have a very basic feathers service which stores data in mongoose using the feathers-mongoose package. The issue is with the get functionality. My model is as follows:
module.exports = function (app) {
const mongooseClient = app.get('mongooseClient');
const { Schema } = mongooseClient;
const messages = new Schema({
message: { type: String, required: true }
}, {
timestamps: true
});
return mongooseClient.model('messages', messages);
};
When the a user runs a GET command :
curl http://localhost:3030/messages/test
I have the following requirements
This essentially tries to convert test to ObjectID. What i would
like it to do is to run a query against the message attribute
{message : "test"} , i am not sure how i can achieve this. There is
not enough documentation for to understand to write or change this
in the hooks. Can some one please help
I want to return a custom error code (http) when a row is not found or does not match some of my criterias. How can i achive this?
Thanks
In a Feathers before hook you can set context.result in which case the original database call will be skipped. So the flow is
In a before get hook, try to find the message by name
If it exists set context.result to what was found
Otherwise do nothing which will return the original get by id
This is how it looks:
async context => {
const messages = context.service.find({
...context.params,
query: {
$limit: 1,
name: context.id
}
});
if (messages.total > 0) {
context.result = messages.data[0];
}
return context;
}
How to create custom errors and set the error code is documented in the Errors API.
I'm setting up a Booking router in NodeJS, and I have many params in.
Now when I forgot params I return an error like :
500: Need more information
I wonder if it's possible to know which params are missing when I return the error code.
This is for a new API made in NodeJS
Here are the params that I want to retrieve from the front ( made in ReactJS )
let body = {
agentDutyCode: "STRING",
RatePlanCode: params.rateCode,
RoomCode: params.roomCode,
AmountAfterTax: params.amountTax,
Start: params.fromDate,
End: params.toDate,
CardCode: params.cardCode,
CardNumber: params.cardNumber,
ExpireDate: params.expireDate,
SeriesCode: params.cvv,
CardHolderName: params.nameCard,
ChainCode: params.chainCode,
HotelCode: params.hotelCode,
RoomQuantities: params.roomQuantities,
GuestQuantitie: params.numberGuest,
GuestPerRoom: params.guestPerRoom,
LastName: params.lastName,
FirstName: params.firstName,
PhoneNumber: params.phoneNumber,
email: params.email,
FVL_SUBUNIT_7: params.walletAddress
}
And this is my promise :
cdsJson.bookResource(req.body)
.then((response) => {
if (response !== null) {
res.response = {
...response
}
} if (response.hotel.length === 0) {
res.respStatus = 500
res.response = {
sendMsg: "Need more informations"
}
next('route')
}
return response
})
If the request succeeds I got a reservation ID otherwise I got :
Error 500: Need more information
Read the documentation or the source code.
Seriously. If the API response doesn't tell you in the error message, then there is no way to know what parameters it expects programmatically.
try it for a for ... in loop like this:
cdsJson.bookResource(req.body)
.then((response) => {
if (response !== null) {
res.response = {
...response
}
} if (response.hotel.length === 0) {
res.respStatus = 500
let errorStr = "Need more informations"
for(var key in req.body) { // Get all parameters that are not set
if(objects[key] == undefined)
errorStr += "\nParameter ["+key+"] is missing!"
}
res.response = {
sendMsg: errorStr
}
next('route')
}
return response
})
You're trying to do server side validation. In Node a good approach would be to define a JSON Schema for the expected parameters and then in your route handler validate the data sent in the request with a JSON Schema validator. This would help you work out whether a request was valid and help you generate error messages automatically. As a rule it's much better (i.e. simpler and more maintainable) to use tools that enable you to declaratively declare your validation (via a schema) than imperatively write code to manually validate objects.
JSON Schema spec https://json-schema.org/
A validator https://github.com/epoberezkin/ajv
The issue here is, that i don't know how to pass some scope.data to expressjs when using ngResource, so then it can be used with express route to insert something to DB.
ExpressJS REST
router.route('/Data')
.get(function(req,res){
var username = req.username;
var collection = db.collection('users');
collection.find({username:username}).toArray(function (err, doc){
res.send(doc[0].pets);
});
})
.post(function(req,res){
!!//I would like to use some data from angular here//!!
var name = req.body.name;
var surname = req.bodysurname;
collection.update({username: username}, {
$push: {
"details": {
name: name,
surname: surname
}
}
}, function (err, result) {
if (err) throw err;
});
});
Angular Factory
(function() {
'use strict';
angular
.module('App')
.factory('Factory', function ($resource) {
return $resource("/Data",{},
{ get:{ method:"GET",
cache:true,
isArray:true},
save:{ method:"POST",
cache:true,
isArray:false
}});
});
})();
Controller.js
This one works fine i use this function with ng-click()
$scope.load = function(){
Factory.get(function (data){
$scope.data = data;
});
};
With this one i have problem i have ng-models name and surname in view and i would like to send them to server so it can be used in REST route as req.body.name and req.body.surname.
$scope.AddData = function(){
Factory.save()
});
};
I think that data should be passed here in this function AddData, however i haven't succeeded yet.
So i tried as Jesus said but without results?
$scope.AddData = function(){
Factory.save($scope.name) //I tried ({name:$scope.name}) too
});
};
After advices from Jesús Quintana I checked the details of method POST and it occurred that everything was all right on angular side i was missing extension of body parser on server Express side
app.use(bodyParser.json())
So now it looks like this
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
NgResource works like a class with private and public methods:
For example Factory.save() is a public method and you must pass the data to be stored in the server, for example:
$scope.AddData = function(factoryData){
Factory.save(factoryData);
});
};
But also have a private method and the above example is the same to this:
$scope.AddData = function(factoryData){
var factory = new Factory(factoryData);
factory.$save(); // Is the same method but is private because factory is a instance of the factory
});
};
Both example are valid methods but must be used of differents ways.
EDIT
I create this little plunkr to see the network request: http://plnkr.co/edit/1bdblyrsW0jr7rXIAVNn?p=
I'm scratching my head on this.
I'm running an expressjs site with angularjs as my front-end resource, and my problem is that I've set up my API to query my datastore with a parameterized query. When I hit the route, the parameter is not appearing in my angularjs resource query, so I end up just getting the entire data set instead of one object by id.
/api/index.js:
var _getSingleRequest = function(req, res, next)
{
models.Request
.findOne({'_id': req.body.id})
.exec(function(err, request){
if(err) return res.send(500, err);
if(!request) return res.send(404, new Error("Request not found"));
res.send(request);
});
};
...
return {
_getSingleRequest: getSingleRequest
}
/server.js
...
var api = require('./api');
app.get('/api/request/:id', api.getSingleRequest);
...
/public/js/controllers/controller.js
...
function Request($scope, $resource, $routeParams, Request)
{
$scope.request = Request.query({_id : $routeParams.id});
...
}
...
/public/js/services/services.js
services.Request = function($resource)
{
return $resource('/api/request/:id', {id:'#id'}, {'get': {method:'GET', isArray: true}});
}
console
Mongoose: requests.find({}) { fields: undefined, safe: undefined, sort: [ [ 'requestedDate', 1 ] ] }
GET /api/request?_id=51b8cc2a06859bd418000001 304 179ms
it appears that the get request is coming through properly, but in the Mongoose query there are no parameters being passed.
what simple mistake am I making, and what other information can I post to help me figure out how to get out of the infinite loop of banging my head on the wall?
In your Request function, you're passing _id:
Request.query({_id : $routeParams.id});
^^^
But in your service, you're expecting id:
$resource('/api/request/:id', {id:'#id'}
^^^
That will generate requests looking like this (which is also what your log says):
/api/request/?_id=XXX
But your backend is expecting this:
/api/request/XXX
req.body.id is for the request body, not route parameters, which come from the request URI. You want req.params.id