Updating in Mongoose thru Postman - node.js

In my mongoose controller, I have something like:
exports.update_a_task = function(req, res) {
Task.findOneAndUpdate({_id: req.params.taskId}, req.body, {new: true}, function(err, task) {
if (err)
res.send(err);
res.json(task);
});
};
And in my PUT command in Postman I put:
url/doSomething/taskId/name //or ,name
But it would only prompt a CastError. How should the URL look like if I want to update a document using the PUT command?

if taskId and name are query params then url becomes
url/doSomething/?taskId=123&name=name

For this url: url/doSomething/taskId, you do something like this:
{
name: "the_name_you_want"
}
Inside of Postman.

To get it in mongoose you need to have the url like this
url/doSomething/:taskId/:name
then in postman you can do a put request to
url/doSomething/taskId/name
now you can use req.params.taskId
if you want to supply the data as /url/doSomething?taskId=&name= you would need to use
req.query.taskId

Related

How can I parse parameters to a RESTful API

I am trying to make a basic RESTful API which works for the most part, but I would like to parse it url parameters such that I can apply multiple filters in one call
I have the following code:
app.get('/api/products/:id',(req, res) => {
let sql = "SELECT * FROM products WHERE id="+req.params.id;
let query = conn.query(sql, (err, results) => {
if(err) throw err;
res.send(JSON.stringify({"status": 200, "error": null, "response": results}));
});
});
which I can use to apply one filter when I send a request like this:
localhost/api/products/1
but I would like to get all products with a name and their categories:
IE I would like to send a request like this:
localhost/api/products?name=stier&category=hammer
I would also like it to accept requests like this
localhost/api/products?category=hammer
Is that possible and how can I adapt my code to do that.
Thank you in advance
You can pass query parameters to that endpoint exactly as you mentioned. You can access those parameters using req.query
for: localhost/api/products?name=stier&category=hammer, req.query will contain:
{
name: 'stier',
category: 'hammer'
}
For that URL, you'll have to add a route for: /api/products and leave /api/products/:id for fetching a single product.
You need only to set '/api/products' the other part behind ? is called parameters and express puts them by itself on the object req.query
app.get('/api/products',(req, res) => {
console.log(req.query)
console.log(req.query.name)
console.log(req.query.category)
})

mongoose, nodejs how to add items into mongo array

I have a collection like
{
id:"david123",
friends[{id:joe321, lname"woo", fname"joe"}]
}
i want to add new elements into friends
i currently have this, but it does not seem to be working
app.post('/users/:uid/friends', function(req, res){
var userId = req.params.uid;
Friend.update({'_id': userId},
{$push: {Friends: req.body.friend}},
{ upsert : true },
function(err, result){
if (err){
console.log(err);
} else {
res.status(200).json(result);
}
})
});
i defined my schema like this
var FriendSchema = new mongoose.Schema({
_id: String,
Friends: [{
_id: String,
fname: String,
lname: String
}]
});
when i make a request i send
{ friend: '{userId:"john123",lname"smoth",fname"john"}',
userId: 'userId123' } and im getting
[TypeError: Cannot use 'in' operator to search for '_id' in {userId:"john123",lname"smoth",fname"john"}]
The sentence "it does not seem to be working" tells us nothing, really. Is there any error printed? Is there bad data inserted? No data? What is the response to the HTTP request? Those are the most important questions but since no relevant info is provided I can only give you some hints:
Make sure that the connection to Mongo is successful
Make sure that you're connecting to the database that you think you are
Make sure you use body parser with correct encoding if needed
Make sure to use $addToSet instead of $push to avoid duplicates
Make sure to return a response on error and not only on success
Make sure you send a request with POST method with JSON content type
Make sure that you send the data in the request body
Make sure that the JSON in your request contains the friend property
Make sure you have some request logging
For (3) see: https://github.com/expressjs/body-parser#bodyparserjsonoptions
For (4) see: https://docs.mongodb.com/manual/reference/operator/update/addToSet/
You didn't say anything about a framework that you use and you did not your question with a framework tag but your code suggests that you may be using Express. If that's the case then to use req.body in the request handlers you need to use a correct body parser middleware:
npm install body-parser --save
and in your code - at the beginning:
const bodyParser = require('body-parser');
and somewhere after you have app but before app.post(...); you need:
app.use(bodyParser.json());
if you want to have the request body parsed as JSON so you could use req.body and req.body.friend in your handler.
And use some basic logging in your request handler:
app.post('/users/:uid/friends', (req, res) => {
// start with:
console.log('Request body:' JSON.stringify(req.body));
// the rest of the logic ...
});
to see what is actually passed in the response and that it is correctly deserialized by the appropriate body parser.

perform a mongoose query in querystring

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?

Node.js: req.params vs req.body

I've been cobbling together code from a few different tutorials to build a basic todo app with the MEAN stack, using node, express, angular, and mongodb. One tutorial covered creating an api for GET, POST, and DELETE actions, but neglected the POST. So, I took it as a challenge to write a function that will update an existing todo. While I got the function working, I encountered an error involving req.params that I didn't understand.
Relevant Code:
Node:
In app.js
app.put('/api/todos/:_id', ngRoutes.update);
which leads to:
exports.update = function(req, res){
var user_id = req.cookies ?
req.cookies.user_id : undefined;
Todo.findByIdAndUpdate(req.params._id,
{ $set: {
updated_at : Date.now(),
content : req.body.formText
}}, function (err, todo) {
if (err)
res.send(err)
Todo.find({ user_id : user_id}, function(err, todos) {
if (err) res.send(err);
res.json(todos);
});
});
};
Angular:
$scope.update = function(id) {
$http.put('/api/todos/' + id, this.todo)
.success(function(data) {
console.log(data);
$scope.todos = data;
})
.error(function(data) {
console.log('Error: ' + data);
});
};
Jade/HTML:
form(ng-submit="update(todo._id)")
input.update-form(ng-show="todo.updating" type="text", name="content", ng-model="todo.formText" placeholder="{{todo.content}}")
This function works fine. It updates the todo in question, and returns the entire list to be reloaded onto the page with the updated value.
However, if in the node code, I change
content : req.body.formText
to
content : req.params.formText
I get the following error as my HTTP response:
Object {
message: "Cast to string failed for value "undefined" at path "content"",
name: "CastError",
type: "string",
path: "content" }
Even while, elsewhere in the function,
req.params._id
works fine to retrieve the todo's '_id' property and use it to find the appropriate document in the database. Furthermore, when viewing the request in Firefox's developer tools, the todo object appears in JSON format under the "Params" tab.
Why does this happen? What is the difference between using req.params vs req.body, and why does the second work and the first not?
req.params is for the route parameters, not your form data.
The only param you have in that route is _id:
app.put('/api/todos/:_id', ...)
From the docs:
req.params
This property is an object containing properties mapped to
the named route “parameters”. For example, if you have the route
/user/:name, then the “name” property is available as req.params.name.
This object defaults to {}.
source: http://expressjs.com/en/4x/api.html#req.params
req.body
Contains key-value pairs of data submitted in the request
body. By default, it is undefined, and is populated when you use
body-parsing middleware such as body-parser and multer.
source: http://expressjs.com/en/4x/api.html#req.body
req.params is the part you send in the request url parameter or the header part of requests.
req.params example in postman
In example above req.params is the data we are sending in postman after ninjas in the
url.
route.delete('/ninjas/:id',function(req,res,next)
{
Ninja.findByIdAndRemove({_id:req.params.id}).then(function(ninja)
{
console.log(ninja.toString());
res.send(ninja);
})
.catch(next);
});
req.body is the part you send in body part of requests
req.body example in postman
req.body is the JSON data we are sending in postman so we can access it in the post request body part.
route.post('/ninjas',function(req,res,next)
{
Ninja.create(req.body).then(function(ninja)
{
console.log("POST"+req.body);
res.send(ninja);
})
.catch(next);
});

Serving dynamic URLs with express and mongodb

I'm building a site that has somewhat reddit-like functionality. I want user-submitted content to get its own page. Each submission is assigned a 5 character ID that I want to be in the URL for that page.
I've got this function in the router file which renders a page called titles:
exports.titles = function(req, res){
i = 0
read(function(post){
url = post[i].URL;
res.render('titles', {title: post[i].title, url: post[i].URL});
});
};
It is served by this statement in app.js:
app.get('/titles', home.titles); //home.js is the router file
The titles page has a link with the text post.title and the URL post.URL. When a user clicks on the link (e.g. domain.com/12345) they should be taken to a page called content with the content post.body.
How do I a)pass the URL back to my app.js file to include in an app.get, b) include the app.get function in this router file, or c) solve this in any other way?
Edit: I do have an object 'titles' that is a mongodb collection, but it is in a different module. No reason I can't add it to the router though.
Edit: I tried adding this to app.js to see if it would work:
app.get('/:id', function(req, res){
return titles.findOne({ id: req.params.id }, function (err, post) {
if (err) throw(err);
return res.render('content', {title: post.title, content: post.body});
});
});
Edit: I got it to work. All I did was format the title so that it would look like domain.com/titles/12345 and change app.get('/:id', to app.get('/titles/:id, ...
If I get you right I would do that the other way around.
Short version
I would get the id from the URL
Then I would pull from the database the data associated with this id
And use this data to build the final page.
You don't need to create a new route for each URL. An URL can contain some variable (here the id) and Express can parse the URL in order to get this variable. Then from this id you can get the data needed to build the proper page.
Long version
I assuming someone type in this URL: http://domain.com/1234.
I also assume that you have a variable titles which is a MongoDB Collection.
You can have a route defined like this:
app.get('/:id', function(req, res) {
// Then you can use the value of the id with req.params.id
// So you use it to get the data from your database:
return titles.findOne({ id: req.params.id }, function (err, post) {
if (err) { throw(err); }
return res.render('titles', {title: post.title, url: post.URL /*, other data you need... */});
});
});
Edit
I made some changes according to the last comments...

Resources