I am building my first express.js application and I have run into my first hurdle.
I have a very simple set up.
routes in app.js:
app.get('/', routes.index);
app.get('/users', user.list);
app.get('/products', product.all);
app.post('/products', product.create);
route(controller) in routes/product.js
var Product = require('../models/product.js');
exports.create = function(req, res) {
new Product({ name: req.query.name, description: req.query.description }).save();
};
exports.all = function(req, res) {
Product.find(function(err, threads) {
if (err) {
res.send('There was an error: ' + err)
}
else {
res.send(threads)
}
});
}
Product model in models/product.js
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var productSchema = new Schema({
name: String,
description: String
});
module.exports = mongoose.model('Product', productSchema);
I have tried sending post requests with both Postman (chrome extension) as well as Curl. I have noticed that both seem to hang after sending the request, as if waiting for a response, i'm no http expert but I assumed a post would not have a response? But perhaps it responds with whether it was successful or not?
my sample requests:
http://0.0.0.0:3000/products?name=Cool, http://0.0.0.0:3000/products?name=Cool%Product&description=Allo%there%Soldier!
After sending the post and then sending a get request to http://0.0.0.0:3000/products I get an array of objects like so:
{
"_id": "52e8fe40b2b3976033ae1095",
"__v": 0
},
{
"_id": "52e8fe81b2b3976033ae1096",
"__v": 0
},
These are equal to the number of post requests I have sent, indicating to me that the server is receiving the post and creating the document/file, but not actually passing the parameters in.
Some help here would be excellent!
EDIT: It seems the code above is fine, I think I may have forgotten to restart my node server after having made some changes (Doh!), the restart fixed the issue
there is something like an http-request lifecycle, and of course an post has a response.
probably something like a 200 if your insert worked and a 404 if not!
you need to send a response in your create method:
exports.create = function(req, res) {
new Product({ name: req.query.name, description: req.query.description }).save();
res.send('saved');
};
Your post needs a response. You could do something like
var newProduct = new Product({ name: req.query.name, description: req.query.description });
newProduct.save(function(err, entry) {
if(err) {
console.log(err);
res.send(500, { error: err.toString()});
}
else {
console.log('New product has been posted.');
res.send(JSON.stringify(entry));
}
});
Related
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've built a simple RESTful API using NodeJS, Mongoose, and Express. I am using the database to store simple string quotes and am not planning to allow access to any other users to the database nor to the api.
I've read up on securing my RESTful API but it seems as if most methods focus on using a username and password to limit access. However, that seems like an overkill for such a simple API especially since i do not consider on allowing anyone else access except for requests that come from the server itself.
So I want to make it so that if anyone else tries to access the API he would be denied access. The only way the API should be accessible is from requests from the server itself i.e from the JavaScript files on the server.
I am currently learning all those things so sorry if i am not using the proper technical terminology :)
I am considering doing something like checking the IP of the person/thing trying to access the API and if that is not the ip of the server then deny access. Would something like this work and how would I got about implementing it.
EDIT: I am looking for something simple since I dont think that most people will take the time to 'hack' the API just so they can access a database of quotes.
Here is my server.js
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var mongoose = require('mongoose');
var Quote = require('./mongodb/models/mainModel.js');
mongoose.connect('mongodb://localhost:27017/myappdb');
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
var port = process.env.PORT || 8080;
var router = express.Router();
function grantAccess(req) {
if(req.ip === '::1' ||
req.ip === '127.0.0.1' ||
req.ip === '::ffff:127.0.0.1') {
return true;
}
return ["IP Address Unknown " + req.ip]
}
router.get('/', function(req, res) {
res.json({ message: 'hooray! welcome to our api!' });
});
router.route('/maindb')
.post(function(req, res) {
var quote = new Quote();
quote.name = req.body.name;
quote.severity = req.body.severity;
quote.createdAt = new Date();
quote.updatedAt = new Date();
quote.save(function(err) {
if (err)
res.send(err);
res.json({ message: 'Quote created!' });
});
})
.get(function(req, res) {
if(grantAccess(req) !== 'boolean')
Quote.find(function(err, quotes) {
if (err)
res.send(err);
res.json(quotes);
});
});
router.route('/maindb/:quote_id')
.get(function(req, res) {
Quote.findById(req.params.quote_id, function(err, quote) {
if (err)
res.send(err);
res.json(quote);
});
})
.put(function(req, res) {
Quote.findById(req.params.quote_id, function(err, quote) {
if (err)
res.send(err);
quote.name = req.body.name;
quote.severity = req.body.severity;
quote.updatedAt = new Date();
// save the bear
quote.save(function(err) {
if (err)
res.send(err);
res.json({ message: 'Quote updated!' });
});
});
})
.delete(function(req, res) {
Quote.remove({
_id: req.params.quote_id
}, function(err, quote) {
if (err)
res.send(err);
res.json({ message: 'Successfully deleted' });
});
});
app.use('/api', router);
app.listen(port);
console.log('Magic happens on port ' + port);
you can add apiKey in your project. It will be required if anyone hits any of your api.
exmaple:
"apiKeys": {
"web": "7fe642cabe6855cd4175937fa8fadd876c1af6b499ab941db6a8a362c0f30f97"
}
similarly you can set apikey for mobile user or accordance to requirment of project.
Link to genrate RandomKey
By this you will allow only those users who have your api key.As api key is shared by you so you will provide it to only appropriate user.
Api key checking:
You can check api key as first middleware before any request to server
example:
router.use(function(req,res,next){
var apiKey = req.get('api_key'); // assuming user will send api key in headers
// code to check api key basic comparison
})
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
I am having trouble with a simple query on my database. Following this tutorial: https://scotch.io/tutorials/build-a-restful-api-using-node-and-express-4 when Model.find() is called, he receives a JSON object back with the name field (the only custom field) and the _id and __v. When I do the same, all I receive back is the _id and __v field. I do get a successful response back saying the post was created, but it doesn't include the title or content fields. Yet a query shows that the data was never saved.
Routing and query:
var express = require("express");
var router = express.Router();
var Post = require("../app/models/post.js");
/* Drop Post collection
Post.remove({}, function(err, num_docs) {
if (err) {
res.send(err);
} else {
console.log("Collection dropped, documents deleted: " + num_docs);
}
});
*/
// Middleware for all routes.
router.use(function(req, res, next) {
console.log("API request made.");
next(); // Go to next routes, don't stop here
});
// Test route to ensure routing is working
router.get("/", function(req, res) {
res.json({
message: "Hooray! Welcome to the API!"
});
});
// On routes that end in /posts
router.route("/posts")
// Create post. (Accessed at POST http://localhost/api/posts)
.post(function(req, res) {
var post = new Post(); // Create new instance of post model
post.title = req.body.title; // Set title (from request)
post.content = req.body.content; // Set content (from request)
// Save the post, and check for errors.
post.save(function(err) {
if (err) {
res.send(err);
} else {
res.json({
message: "Post created!",
title: post.title,
content: post.content
});
}
});
})
.get(function(req, res) {
Post.find({}).exec(function(err, posts) {
if(err) {
res.send(err);
} else {
res.json(posts);
}
});
});
module.exports = router;
Response:
[
{
"_id": "56a6adc31f06c4dc1cf82888",
"__v": 0
},
{
"_id": "56a9768888f806dc1fe45415",
"__v": 0
},
{
"_id": "56a97f3f4e269b7c21311df8",
"__v": 0
}
]
A db query in the shell returns the same information, just an _id and __v field.
I am beyond confused right now. It suddenly works, the code is the exact same as above. I am going to leave this open in case someone stumbles across it one day and can solve this mystery.
The problem is that you need to set the content-type while sending the post request as application/json otherwise the fields are not recognized.
I experienced an error like this.
The problem was that I was loading the wrong mongoose Schema.
The result is that the only fields that are saved are those that are present in both schemas, i.e. _id and __v.
For this code
post.title = req.body.title; // Set title (from request)
post.content = req.body.content; // Set content (from request)
could you check
req.body.title and req.body.content are not undefined?
do you set the field in your Post schema as
var PostSchema = new Schema({
title: String,
content: String
});
If you are using a manual tool like Postman to test your app, you must also put quotes around the key names in the body of your request, like {"key": "some string"}.
If you just put {key: "some string"} then the whole key/value pair is ignored when the document is saved to the database.
Exact same thing happened to me...
First two POSTs succeeded but did not post the data I was sending:
var p = new Post();
p.result = 'hello-world';
p.save(function (err) {});
Turned on debug mode: mongoose.set('debug', true); and next POST the field was saved...
Baffling!
I'm trying to do a POST request using express on node.js and mongoose for mongoDB but using Postman to get data gives me this error:
Error at MongooseError.ValidationError
(C:\Users\Matteo\Desktop\app1\node_modules\mongoose\lib\error\validation.js:22:16)
at model.Document.invalidate
(C:\Users\Matteo\Desktop\app1\node_modules\mongoose\lib\document.js:1162:32)
at
C:\Users\Matteo\Desktop\app1\node_modules\mongoose\lib\document.js:1037:16
at validate
(C:\Users\Matteo\Desktop\app1\node_modules\mongoose\lib\schematype.js:651:7)
at
C:\Users\Matteo\Desktop\app1\node_modules\mongoose\lib\schematype.js:679:9
at Array.forEach (native) at
SchemaString.SchemaType.doValidate
(C:\Users\Matteo\Desktop\app1\node_modules\mongoose\lib\schematype.js:656:19)
at
C:\Users\Matteo\Desktop\app1\node_modules\mongoose\lib\document.js:1035:9
at process._tickCallback (node.js:355:11)
I paste here my server.js file
var express = require('express')
var bodyParser = require('body-parser')
var mongoose = require('mongoose');
var app = express()
app.use(bodyParser.json())
mongoose.connect('mongodb://localhost/social', function(){
console.log('mongodb connected')
})
var postSchema = new mongoose.Schema ({
username : { type: String, required: true },
body : { type: String, required: true },
date : { type: Date, required: true, default: Date.now}
})
var Post = mongoose.model('Post', postSchema)
app.get('/api/posts', function(req, res, next){
Post.find(function(err, posts){
if(err) { return next(err) }
res.json(posts)
})
})
app.post('/api/posts', function(req, res, next){
var post = new Post({
username : req.body.username,
body : req.body.body
})
post.save(function(err, post){
if(err){ return next(err) }
res.json(201, post)
})
})
app.listen(3000, function(){
console.log('Server listening on', 3000)
})
Can anyone help me or it is a problem of mongoose?
I got a similar error today, and reading the docs helped me through.
The doc says:
Defining validators on nested objects in mongoose is tricky, because nested objects are not fully fledged paths.
var personSchema = new Schema({
name: {
first: String,
last: String
}
});
A schema like the above will throw similar error like in your question.
The docs points out the workaround:
var nameSchema = new Schema({
first: String,
last: String
});
personSchema = new Schema({
name: {
type: nameSchema,
required: true
}
});
Source: http://mongoosejs.com/docs/validation.html
in Postman, to test HTTP post actions with raw JSON data, need to select the raw option and also set the header parameters( select header parameter in postman and add in key:value feild which is given below ).
Content-Type: application/json
by default, it comes with Content-Type: text/plain which need to replace with Content-Type: application/json
I think it's Mongoose Validation issue, req.body is actually a JSON formatted-data, console.log(req.body.username) returns the username value. To be general, try to use req.body, please make use to fill-out the required fields upon submission.
app.post('/api/posts', function(req, res, next){
var post = new Post(req.body);
post.save(function(err, post){
if(err){ return next(err) }
res.json(post)
})
})
Better to handle MongooseError.ValidationError when a user fails to fill-in required fields. Try to review your Mongoose Model.