I have two separate app.get routes that render "/" and contain mongoose find{} methods, which relates to two mongodb collections. I then have corresponding post routes that redirect/post to "/" with the data I entered on the forms.
Depending on the form I use (devportals/new or writingportals/new) to submit my data, I would like it to use the correct app.get to find the data and then populate my index.ejs file to display the new entry.
When I submit my post, it always defaults to the devportal.find{} GET route at the top even though I submitted my update to mongodb through the writingportals form.
app.get("/", function (req, res){
devportal.find({}, function(err, newDevPortal){
if(err){
console.log(err);
}else{
res.render("index",{newDevPortal: newDevPortal});
}
});});
app.get("/", function (req, res){
writingportal.find({}, function(err, newWritingPortal){
if(err){
console.log(err);
}else{
res.render("index",{newWritingPortal: newWritingPortal});
}
});
});
app.get("/portals/writingportals/new", function(req, res){
res.render("portals/writingportals/new");
});
app.get("/portals/devportals/new", function(req, res){
res.render("portals/devportals/new");
});
app.post("/", function(req, res){
var wpTitle = req.body.writingtitle;
var wpUrl = req.body.writingurl;
var wpImageUrl = req.body.writingimageurl;
writingportal.create({title: wpTitle, url: wpUrl, imageurl: wpImageUrl}, function(err, newWritingPortal){
if(err){
console.log(err);
}else{
res.redirect("/");
}
});
});
app.post("/", function(req, res){
var devTitle = req.body.writingtitle;
var devUrl = req.body.writingurl;
var devImageUrl = req.body.writingimageurl;
devportal.create({title: devTitle, url: devUrl, imageurl: devImageUrl}, function(err, newDevPortal){
if(err){
console.log(err);
}else{
res.redirect("/");
}
});
});
I have two separate app.get routes that render "/"
Your endpoints do not render "/", they get triggered based on the request uri, which in your case is "/".
Next, you cannot have two endpoints with the same route and same type of request, this is true with any web server. So in your case, you have two endpoints defined for GET requests to '/'. There is no way for your code to understand the difference between those two routes, so it will always hit the first one it finds.
Finally I'll add that for your solution, you most definitely will want unique endpoints because you are taking a template with a data model and sending back markup.... basically an endpoint for each page.
I will say that I'm not a huge fan of that approach. I think it makes life easier using a framework like react, then have every request to '/' return index.html with a reference to your react code... instead of the whole SSR deal.
However, for a lot of things, its simple to just use query parameters in the request. Here is an example where one endpoint is defined that will return all data from any collection through query parameters:
app.get('/api/collection', function(req, res) {
var collection = req.query.collection;
var _collection = db.collection(collection);
_collection.find({}).toArray(function(err, docs) {
if (err)
return res.status(500).send(err);
res.send(docs);
})
})
And then the request url would be GET -> /api/collection?collection=NAME_OF_YOUR_COLLECTION
Related
The existing code was written as MySQL query and I am now working on converting it to Mongoose query.
I need to get five data sorted by the most recent subscription year from the main page.
The existing code brought this result value into an array. And data was delivered through pug view, and Mongoose seems to bring the result value of Object. In this case, I wonder how to deliver the data through Pug view.
I checked importing data from the terminal to the console.log, but an error called 'Error [ERR_HTTP_HEADERS_SENT]: Cannot set heads after they are sent to the client occurs and no data is passed to the pug. I wonder why this problem occurs.
[MySQL Query]
router.get("/", function (req, res, next) {
// Main page Profile Data Process
db.query(`SELECT * FROM user ORDER BY registerDate DESC LIMIT 5`, function (
error,
data
) {
// Log Error
if (error) {
console.log(error);
}
res.render("main", {
dataarray: data,
_user: req.user,
url: url
});
});
});
[Mongoose Query]
router.get("/", function (req, res, next) {
let dataarray = [];
let userData = db.collection("user").find().limit(5).sort({
"created_at": -1
});
userData.each(function (err, doc) {
if (err) {
console.log(err);
} else {
if (doc != null) {
dataarray.push(doc)
}
}
// console.log(dataarray.login)
console.log(dataarray);
res.render("main", {
dataarray,
_user: req.user
})
});
});
[pug file]
each profile in dataarray
.col-lg-4
img.rounded-circle(src=`${profile.avatar_url}` alt='Generic placeholder image' width='140' height='140')
h2=`${profile.login}`
p=`${profile.bio}`
p
a.btn.btn-secondary(href=`/${profile.login}` role='button') View details ยป
You are sending the request in multiple chunks, node/express uses one request and one response.
Cannot set heads after they are sent to the client
Is the error that happens when the res.render is called the second time. At this point, the one request has already left the node/express process and this is tell you that you're trying to violate the one request/one response paradigm.
This is the part of your code where you can see why this happens.
router.get("/", function (req, res, next) {
let dataarray = [];
let userData = db.collection("user").find().limit(5).sort({
"created_at": -1
});
userData.each(function (err, doc) {
This part of your code will try to send a response for each item in your resultset.
Something like this will work properly (I didn't test it):
router.get("/", function (req, res, next) {
db.collection("user").find().limit(5).sort({ "created_at": -1 }, function(err, userData){
res.render("main", {
dataarray: userData,
_user: req.user
})
});
});
In other words, only one res.render is required and pass the entire result set into that.
I'm making an API call in a POST route but for some reason, I can't pass the JSON data through res.render in the POST route. So I'm thinking about passing the JSON object to GET route so I can render it to the right client page.
Heres my GET and POST routes:
router.get('/bookDetails', (req, res) => {
res.render('bookDetails');
});
router.post('/bookDetails', (req, res) => {
let ID = req.body.ID;
request('https://www.googleapis.com/books/v1/volumes/' + ID, (err, response, body) => {
if(!err && response.statusCode == 200){
let bookdata = JSON.parse(body);
res.render('bookDetails', {bookdata: bookdata});
}else{
console.log(err);
}
});
});
I can't read the bookdata in my bookDetails.ejs file? Is there another way pass this data to the page?
On semantic, it should be a GET router to display something about the ID resource.
router.get('/bookDetails/:id', (req, res) => {
let resource = await fetchResourceById
res.render('bookDetails', resource);
});
also, you can define a middleware function to reuse the fetchResource logic, as following:
function fetchResourceMiddleware(){
return function(req, res, next){
var id = req.query.id || req.body.id
if(id){
req.resource = await fetchResource(id)
}
next()
}
}
reuse the middleware function for GET and POST router:
function renderResource(req, res){
res.render('bookDetails', req.resource);
}
router.get('/bookDetails/:id', fetchResourceMiddleware(), renderResource)
router.post('/bookDetails', fetchResourceMiddleware(), renderResource)
hope helpful, good luck!
After post, your get method will run.
In the get method, you are not sending any data to ejs template, so it will not detect it.
You should redirect in post method, it is bad idea sometimes,
Using ReactJS, Redux, Webpack, Node.js and Express with MongoDB, I am following the tutorial https://github.com/vasansr/mern-es6 and trying to integrate it into my project. First, I am trying to make a POST request to the server I created. And it gets a response with a success and no error is logged. Yet inside the server POST API, it does not log console.log('Req body', req.body);, and in terminal I checked to see if the database has been created with mongo -> show dbs but it is empty.
Could it be that something is intercepting the request from the server? What could be the issue?
This...
app.use('/', function (req, res) {
res.sendFile(path.resolve('client/index.html'));
});
comes before:
app.post('/api/users/', function(req, res) {
//...
});
Since it's app.use the POST /api/users will still hit that middleware, and res.sendFile ends the request/response. You'll probably see that your post is getting back the client HTML.
Try moving your client HTML endpoint to the end of your middleware, just before the error handlers if you have them. That way, it'll only get used if none of your API endpoints match. Or if you want just GET / to return the HTML, change use to get:
app.use(webpackDevMiddleware(compiler, {noInfo: true, publicPath: config.output.publicPath}));
app.use(webpackHotMiddleware(compiler));
app.use(express.static('dist')); //where bundle.js is
app.use(bodyParser.json());
app.post('/api/users/', function(req, res) {
console.log('Req body', req.body);
var newUser = req.body;
db.collection('users').insertOne(newUser, function(err, result) {
if(err) console.log(err);
var newId = result.insertedId;
db.collection('users').find({_id: newId}).next(function(err, doc) {
if(err) console.log(err);
res.json(doc);
});
});
});
app.get('/', function (req, res) {
res.sendFile(path.resolve('client/index.html'));
});
app.post('/api/users/', function(req, res) {
console.log('Req body', req.body);
var newUser = req.body;
db.collection('users').insertOne(newUser, function(err, result) {
if(err) console.log(err);
var newId = result.insertedId;
db.collection('users').find({_id: newId}).next(function(err, doc) {
if(err) console.log(err);
res.json(doc);
});
});
});
I have a small comments about this code, for if(err) console.log(err); i think you should change to if(err) return console.log(err);.
For error case, i think you need return, otherwise the below part will be excuted, and there will report some error.
I have this middleware:
app
.use('/:lang?', middleware.setLanguage)
.use('/thanks/:lang?', middleware.setLanguage)
.use('/forum/:lang?', middleware.setLanguage);
I want use a function called setLanguage in every route that have lang?, and currently this code is working, but i can believe dry that.
Someone knows how to? I search in the documentation, but didn't find anything..
Use .param to process the parameters up front so that you don't have to do it on every route that uses it. For example, here's one that starts building a query with moongoose for a product crud:
app.param('product_id', function (req, res, next, product_id) {
req.product_id = new ObjectId(product_id);
req.product = Product.findOne(req.product_id);
next();
});
// update product
app.put('/products/:product_id', function (req, res) {
Product.findOneAndUpdate(req.product_id, req.body, function (err, product) {
res.json(product.toObject());
});
});
// get product by id
app.get('/products/:product_id', function (req, res) {
req.product.lean().exec(function (err, product) {
res.json(product);
});
});
None of my routes have to get the product id and convert it to an ObjectId because i did that up front for all routes with .param.
I am making a wikipedia clone for a project. My initial edit Route looks like this:
router.get('/edit/:id', function(req, res){
var id = req.params.id;
console.log(id);
models.Page.findById(id, function(err, doc){
console.log(doc);
res.render('edit', {page: doc});
});
});
All I am doing is creating an edit page view for a Page with matching id of the id params.
This works until...
I had to add this new route:
router.get('/:url_name/:id', function(req,res){
var id = req.params.id;
models.Page.findById(id, function(err, doc){
res.render('show_page', {page: doc});
});
});
Now when I have this route active, my edit/:id page route doesn't collect the id parameter(req.params.id).
I am lost on why this isn't working and where I should start trying to debug because I am not getting any errors, it will still take me to my edit/:id page, but when I console.log(id) I do not receive a value, or even and undefined, nothing. Furthermore, the {page:doc} I am passing to my edit view is not being found.
If anyone can provide insight or a place to start looking to debug, I'd appreciate it. Just a reminder, the edit/:id route works as it should (req.params.id grabs the id) when I don't have the :url_name/:id route active.
Current Routes -
I added var wiki_routes = require('./routes/wiki'); in my app.js and in that route I have:
// **** URL ROUTES ****
router.get('/', function(req, res) {
models.Page.find(function(err, docs) {
res.render('index', { docs: docs });
});
});
router.get('/:url_name', function(req, res){
var url_name = req.params.url_name;
var isUpdated = req.query.updated;
var updated = (isUpdated === 'true')?true:false;
models.Page.find({url_name: url_name}, function(err, page){
if(page.length > 1){
console.log(page);
res.render('disambiguation', {pages: page, updated: updated });
} else {
console.log(page);
res.render('show_page', {page: page[0], updated: updated});
}
});
});
router.get('/:url_name/:id', function(req,res){
var id = req.params.id;
models.Page.findById(id, function(err, doc){
res.render('show_page', {page: doc});
});
});
// **** EDIT ROUTES ****
router.get('/edit/:id', function(req, res){
var id = req.params.id;
console.log(id);
models.Page.findById(id, function(err, doc){
console.log(doc);
res.render('edit', {page: doc});
});
});
router.post('/edit_submit/:id', function(req, res){
var id = req.params.id;
var new_title = req.body.title;
var new_body = req.body.body;
console.log(req.body);
models.Page.findByIdAndUpdate(id, {title: new_title, body: new_body }, function(err, docs){
// redirects to the wiki page
res.redirect('/wiki/'+ docs.url_name +'?updated=true');
});
});
// **** DELETE ROUTE ****
router.get('/delete/:id', function(req, res){
var id = req.params.id;
models.Page.findByIdAndRemove(id, function(err, data){
res.redirect('/?deleted=true');
});
});
Routes are set up as they occur in the code, as node will give presedence to whichever route it encounters first, so order matters a lot when you're setting up your routes.
An URL could potentially match several routes, especially when using variables that catch a large number of different URL's, or static routes etc.
Say in your case you have an URL that looks like
http://example.com/edit/1234
That URL would most certainly be caught by this route
router.get('/:url_name/:id' ....
as it matches the http://example.com/something/something layout, and it would also be caught by the following route
router.get('/edit/:id', ....
as it matches the http://example.com/edit/something layout.
What route actually cathes the URL depends on the order they where encountered when set up, whichever route that was declared first will catch the URL.
Just shifting the order of the routes will in most cases solve issues like this
// if the URL matches, this will execute first
router.get('/edit/:id', function(req, res){
// do stuff
});
// You'll only get here if the URL doesn't match the above route
router.get('/:url_name/:id', function(req, res){
// do stuff
});
There is a workaround if you simply can't swap the routes around, using the next() callback, like this
router.get('/:url_name/:id', function(req, res, next){
if ( req.params.url_name == 'edit' ) {
next(); // this sends the request back to look for routes below this one
}else{
// do stuff
}
});
router.get('/edit/:id', function(req, res){
// now we'll get here when the "url_name" is "edit" ...
});