I'm trying to do a simple mongoose query using the query string.
This works
router.get('/', function(req,res) {
myModel.find({name:"test e"}, function(err,data){
if(err) console.log(err)
res.json(data);
});
});
This doesn't work (I get the whole collection)..
router.get('/', function(req,res) {
console.log(req.query.q)
myModel.find(req.query.q, function(err,data){
if(err) console.log(err)
res.json(data);
});
});
with this request
/api/myModel?q={name:"test e"}
I don't think it's an url encoding issue since I print the 'q' var and it looks fine server side.
Side question: if this isn't the standard mode, what's the RESTful standard way to query a db?
Edit for more general details:
I don't need a simple access by id or name like Ashley B suggests, I need a proper search engine for my db (the user, using a graphic web interface, have to be able to query each field separately, potentially)
Edit 2:
thanks to Blakes Seven I solved my initial problem, but if you know or use a better way to perform a complex query I would happy to discuss. Maybe I should expose anther resource "/api/seach"?
I think I can answer your first question by answering your second (side question).
Let's say you have a User model, normally you'd have an endpoint to get all users, like so:
router.get('/users', function(req, res) {
// find all users
users.find({}, function(err, data){
if(err) console.log(err)
res.json(data);
});
});
To find a specific user you then have an endpoint like so:
router.get('/users/:name', function(req, res) {
// get the user's name from the url and find that user
users.find({name: req.params.name}, function(err, data){
if(err) console.log(err)
res.json(data);
});
});
So rather than passing the whole query through the query string, you just find use a specific part. Allowing the users to directly access your data with their own queries makes it much much harder to secure.
I would recommend you to use some library to parse the querystrings to mongoDB queries. It would fix your problem and make your code better.
querymen would help you by transforming /myModel?q=test+e into {name: 'test e'}, giving you full control over the querystring schema.
var querymen = require('querymen')
// querymen.middleware() defines an express middleware with querystring schema
router.get('/', querymen.middleware({
q: {
type: String,
paths: ['name']
}
}), function(req, res) {
console.log(req.querymen.query) // {name: 'test e'}
myModel.find(req.querymen.query, function(err,data){
if(err) console.log(err)
res.json(data);
});
});
The proper query should look like this:
/api/myModels?name=test%20e
The myModals part is in plural.
Check here: How to design RESTful search/filtering?
Related
This is a typical route in node.js that has a pseudo-code to connect to a database, get some data using a query and then pass them to a page to be rendered,
router.get('/', function(req, res){
db-connect(function(err, db) {
if (err) {
return console.log('error');
}
db.query('select * from table', function(err, results){
if (err) {
return console.log('error');
}
res.render('index',{
'title':'my title',
'pageHeader': 'my header',
'results': results //dynamic ???
});
});
}); //connect
});//router get
I am using this pseudo-code to ask a general question :
The results data are dynamic, maybe the query will take a while, so the results do not get to the page fast, so I guess the rendering will also take a while.
How can I render static data immediatly (title and pageHeader) and the dynamic part (results) as soon as it is ready ?
Do I have to use another function or another syntax?
Thank you
res.render populates your template and sends it to the client (browser). You cannot send "a bit more" when it's ready at a later stage.
Either make the client wait for the data, or send your title and header first, and use XHR (javascript) on the browser to get the rest.
I'm using nodejs and express and I have a navigation menu that is built using data that is in mongodb currently I'm just making a call to the database to get a list of companies and passing that back inside each route. There doesn't seem to be a way to store this information in localstorage client side. So I"m wondering what is the most effective way to handle this situation. Sample of my code
admin.get('/', function(res, resp){
mongodb.connect(url, function(err, db){
var collection = db.collection('companies')
collection.find({}).toArray(function(err, companies){
res.render('adminview', {companies:companies})//once the page is rendered I would like to save the company list to localstorage.
})
})
})
admin.get('/:company', function(res, resp){
/* repeating code from above because I need this list */
mongodb.connect(url, function(err, db){
var collection = db.collection('companies')
collection.find({}).toArray(function(err, companies){
/* more code to do stuff to render the company page */
res.render('companyadminview', {companies:companies, company:company})
}) })
I could be going about this the wrong way I'm new to web development this feels wrong to me but can't figure out a different way.
So, first off you should be able to store it in localstorage or sessionstorage just fine, unless you're targeting browsers that don't support it.
That said, I think it's best not to, as the fact that you're storing it in the DB implies that it changes with enough frequency that you will get buggy clientside behavior if you cache it there for too long.
Instead, I'd just setup a middleware and attach it to the locals object on a per request basis, unless you want to do some kind of cache on the server:
app.use(function(req, res, next) {
mongodb.connect(url, function(err, db){
if (err) return next(err);
var collection = db.collection('companies')
collection.find({}).toArray(function(err, companies){
if (err) return next(err);
res.locals.companies = companies;
next();
});
});
});
See the example below:
var apiRouter = express.Router();
apiRouter.post('/api/postAgree', function(req, res, next){
userModel.findOneAndUpdate(
{profileID: req.session.facebookProfileId},
{$push:{postsAgreed: req.query.postID}},
{safe: true, upsert: true},
function(err, model) {
if (err){
console.log(err);
}
}
)
Now, the MongoDB operation is already done and I want to stay on the same page.
Will I be doing this:
res.render('theSamePageIamOn', {foo:bar});
I know this works but it seems like it is a very inefficient way of doing it.
So my question really is: If I have a button on a page which makes an API call but I want to stay on the same page, how will I do that? The res.(options) function sort of is made like it has to take me to other pages
Thanks to #robertklep and #GiladArtzi - it should be an AJAX call and the response should be in the form of:
res.json()
Then the response can be handled by the frontend using other tools like: Angular
I'm not sure what you're talking about, just call the function....
function doesSomething (args) {
console.log(args)
}
apiRouter.post('/api/postAgree', function(req, res, next){
doesSomething("HELLO")
});
Function calls don't expects the user to go to another page each time an API call is handled.
I'm converting an MS Access database to a webapp. I'm using Angular JS, Node JS with the express framework and MySQL as database.
In ms access you don't have any edit/save features. When you edit something, the database changes instantly. I like this. Feels smooth. So I want to have this the same way in the web app. My question is. Will there be any problems with this approach in my webbapp?
This is a piece of my node js code which updates the database with a restcall:
/*
Post /api/products/ HTTP/1.1
*/
exports.editProduct = function(req, res) {
console.log(req.body);
var post = [{title_en: req.body.title_en},req.params.id];
if (connection) {
connection.query("UPDATE products SET ? WHERE id = ?", post, function(err, rows, fields) {
if (err) throw err;
res.contentType('application/json');
res.write(JSON.stringify(rows));
res.end();
});
}
};
And on the client side I use the a the $resource object
$scope.save = function(){
$scope.product.$save(function(){
console.log('Save successfull);
});
};
And in the view. I simply have inputs with ng-change:
<input ng-model="product.title_en" ng-change="save()".
Will this work good in production mode with a couple hundred users? Is the chances of blocking/crashing etc?
The only thing I see is if (err) throw err;
if there is an error the server crash so change it with a json response with a 500 status.
By the way express has a build-in way to output json
It's better off to validate title_en and id
exports.editProduct = function(req, res) {
console.log(req.body);
var post = [{title_en: req.body.title_en},req.params.id];
if (connection) {
connection.query("UPDATE products SET ? WHERE id = ?", post, function(err, rows, fields) {
if (err) {
return res.json(500,{ error: 'Cannot update the product' });
}
res.json(200,rows);
});
}
an other thing try to use restangular instead of resource it's a lot of fun :)
};
This is a pure best practice question. I am pretty new to Node and Mongoose. I absolutely love the technology and have been cranking away on a project to build a JSON-backed API for an app that I'm building.
I am finding that I am continuously repeating code when I fetch objects from my database. For example:
Playlist.findById(req.params.id, function(err,playlist){
if (err)
return res.json({error: "Error fetching playlist"});
else if (!playlist)
return res.json({error: "Error finding the playlist"});
//Actual code being performed on the playlist that I'm fetching
});
The error handling at the top of the function call is annoying because I have to repeat that code for every call to the database... or so I think.
I thought about using a callback like:
var fetchCallback = function(err,objOrDoc,callback){
//Handle the error messages
callback(objOrDoc);
};
However, this approach would mess up my sequential flow since I would have to define the callback function before I performed the fetch. So, if I had a lot of database queries chained together, I would have to place the callbacks in reverse order, which is far from ideal in a clean-coding perspective.
I'm wondering if anyone has run into this issue and has any best practices for cutting down on the repetition.
I'm also using the express framework, so if there's a helpful way to handle it in express, I'd be interested to know, too.
There are a couple interesting approaches you could try here.
At the most simple, you could simply have a function that loads up an object and handles the output in an error condition.
fetchResource = function(model, req, res, callback) {
model.findById(req.params.id, function(err, resource) {
if (err)
return res.json({error: "Error fetching " + model.toString()});
else if (!playlist)
return res.json({error: "Error finding the " + model.toString()});
callback(resource);
});
};
app.on('/playlists/1', function(req, res) {
fetchResource(Playlist, req, res, function(playlist) {
// code to deal with playlist.
});
});
That's still quite a bit of duplication, so I might try to move this out into a middleware.
Route Middleware
Routes may utilize route-specific middleware by passing one or more additional callbacks (or arrays) to the method. This feature is extremely useful for restricting access, loading data used by the route etc.
Now I haven't tested this and it's a bit hand-wavey (read: pseudocode), but I think it should serve as a decent example.
// assuming a URL of '/playlist/5' or '/user/10/edit', etc.
function loadResource(model) {
return function(req, res, next) {
model.findById(req.params.id, function(err, resource) {
if (err)
return res.json({error: "Error fetching " + model.toString()});
else if (!resource)
return res.json({error: "Error finding the " + model.toString()});
req.resource = resource;
next();
});
}
}
app.get('/playlist/:id', loadResource(Playlist), function(req, res) {
var playlist = req.resource;
...
});
app.get('/user/:id', loadResource(User), function(req, res) {
var user = req.resource;
...
});
The express source contains a pretty good example of this pattern, and the middleware section in the docs (specifically under 'Route Middleware') details it further.