ExpressJS: How to send data to URL - node.js

I have a post request which redirects to a new route as you can see
//POST login
app.post('/login', urlencodedParser, function(req,res){
userLogin(req.body, function(response){
if(response == true){
//Activate player login in db
loginPlayerDB(req.body.username);
getPlayerId(req.body.username, function(id){
res.redirect(req.baseUrl + '/:id/profile');
});
} else{
res.send(500, "Username or Password is incorrect");
}
})
});
This function gets called
function getPlayerId(username, callback){
let sql = "SELECT PlayerId FROM users WHERE Username = (?)";
var values = [username];
db.query(sql, values, function(err, result, fields){
if(err){
console.log(err);
} else{
return callback(result);
}
})
}
It redirects to this route
app.get('/:id/profile', function(req,res){
res.render('profile/profile');
})
Everything works great except the URL on the new page is
http://localhost:3000/:id/profile
When it should be something like
http://localhost:3000/6/profile
If the player has an id of 6. How do I fix this
//Solution.
Thank you to MadWard for the help. My mistake was that in my getPlayerId function, I return result, which is an array, instead of result[0].PlayerId in order to get the specific id I was looking for

You are literally redirecting to /:id/profile. This is a string, it is fixed, and will always be '/:id/profile'.
What you want to do instead is, using template strings:
getPlayerId(req.body.username, function(id){
res.redirect(`${req.baseUrl}/${id}/profile`);
});
Using normal string concatenating like you were doing:
getPlayerId(req.body.username, function(id){
res.redirect(req.baseUrl + '/' + id + '/profile');
});

Related

How to access parent modules when doing layered routing in mongoDB

As a personal project, I'm trying to build a social media site for teddy bear collectors. I would like users to be able to make a "collection" page which they can populate with individual profile pages for each of their bears. Finally, I would like other users to be able to comment on both the collection page and the individual profile pages.
However, I'm running into an error on the "/new" route for comments on the individual profile pages. I can't get it to find the id for the parent collection.
Below is the code I'm working with. I start by finding the id for the collection, then I get try to get the id for the bear (individual profile page). However, the process keeps getting caught at the first step.
var express = require("express");
var router = express.Router({mergeParams: true});
var Bear = require("../models/bear");
var Comment = require("../models/comment");
var Collection = require("../models/collection");
var middleware = require("../middleware");
//comments new
router.get("/new", middleware.isLoggedIn, function(req, res) {
Collection.findById(req.params.id, function(err, foundCollection) {
if(err || !foundCollection){
req.flash("error", "Collection not found");
return res.redirect("back");
}
Bear.findById(req.params.bear_id, function(err, foundBear) {
if(err){
res.redirect("back");
} else {
res.render("bcomments/new", {collection_id: req.params.id, bear: foundBear, collection: foundCollection});
}
});
});
});
//bcomments create
router.post("/", middleware.isLoggedIn, function(req, res){
Collection.findById(req.params.id, function(err, foundCollection) {
if(err || !foundCollection){
req.flash("error", "Collection not found");
return res.redirect("back");
}
//look up bear using id
Bear.findById(req.params.id, function(err, foundBear){
if(err){
console.log(err);
res.redirect("/bears" + bear._id);
} else {
//create new comment
Comment.create(req.body.comment, function(err, comment){
if(err){
req.flash("error", "Something went wrong");
console.log(err);
} else {
//add username and id to comment
comment.author.id = req.user._id;
comment.author.username = req.user.username;
//save comment
comment.save();
//connect new comment to bear
bear.comments.push(comment);
bear.save();
//redirect bear show page
req.flash("success", "Successfully added comment");
res.redirect("/collections/" + foundCollection._id + "/bears/" + foundBear._id);
}
});
}
});
});
});
So, instead of rendering the new comment form, it hits a "null" error and redirects back at the first if statement.
If anyone can help me figure this out, I'd be exceedingly grateful.
Thank you.
I think the problem is that you are defining the path "/new" (which has no parameters) and trying to access req.params.id. If you expect to have the parameter id you should define it in the path like this: router.get("/new/:id", .... Check the Express oficial documentation for more details.
EDIT:
You may have mixed req.params with req.query and req.body. If you are passing parameters in the request, you must access them through req.query (for example: req.query.id or req.query.bear_id) in the case of GET and DELETE or through req.body in POST and PUT.

GET request with different mongo find results on same page

I would like my website to have a search bar in the top section that returns a single document (ink) from a mongo database. On the same page, I would like to be able to access all documents from the same database.
I'm having trouble trying to figure out how to do this on one page, since I can only send one result to URL.
Is there some way to send all documents to the page, then do a search with AJAX on the client side? I'm new to coding, and wondering if I'm going about this wrong.
I appreciate any help. Here is part of my code that sends the results I want, but to different pages.
app.get("/", function(req, res){
// FIND ONE INK FROM DB
var noMatch = null;
if(req.query.search) {
Ink.find({ink: req.query.search}, function(err, foundInk){
if(err){
console.log(err);
} else {
if(foundInk.length < 1) {
noMatch = "No match, please try again.";
}
res.render('new-index', {ink: foundInk, locationArray: locationArray, noMatch: noMatch })
}
});
} else {
// FIND ALL INKS FROM DB
Ink.find({}, function(err, allInks){
if(err){
console.log(err);
} else {
res.render("index", {ink: allInks, locationArray: locationArray, noMatch: noMatch });
}
});
}
});
You can use separated endpoints for each request. For the full access request, you can render the page, calling res.render, and for the search request, you can return a json calling res.json. Something like this:
app.get("/", function(req, res){
// FIND ALL INKS FROM DB
Ink.find({}, function(err, allInks){
if(err){
console.log(err);
} else {
res.render("index", {ink: allInks, locationArray: locationArray, noMatch: noMatch })
}
});
})
app.get("/search", function(req, res) {
// FIND ONE INK FROM DB
var noMatch = null;
Ink.findOne({ink: req.query.search}, function(err, foundInk){
if(err){
console.log(err);
} else
if(!foundInk) {
noMatch = "No match, please try again.";
}
res.json({ink: foundInk, locationArray: locationArray, noMatch: noMatch })
}
});
});
Note the call to Ink.findOne in the /search handler, which will return only one document.
This way you can make and AJAX request to /search, and parse the json returned from the server.
I've created a sample repository with the exact same issue here
Ideally you make an endpoint like this.
( id parameter is optional here...thats why the '?' )
www.example.com/api/inks/:id?
// return all the inks
www.example.com/api/inks
// return a specific ink with id=2
www.example.com/api/inks/2
So now you can render all the links via /inks and search a particular ink by using the endpoint /ink/:id?
Hope this helps !

How To Bind Node-js DB Query to Web Form

I'm using node and postgres, I'm new to writing async function, what I'm trying to do is a very simple query that will do a total count of records in the database, add one to it and return the result. The result will be visible before the DOM is generated. I don't know how to do this, since async function doesn't return value to callers (also probably I still have the synchronous mindset). Here's the function:
function generateRTA(callback){
var current_year = new Date().getFullYear();
const qry = `SELECT COUNT(date_part('year', updated_on))
FROM recruitment_process
WHERE date_part('year', updated_on) = $1;`
const value = [current_year]
pool.query(qry, value, (err, res) => {
if (err) {
console.log(err.stack)
} else {
var count = parseInt(res.rows[0].count) + 1
var rta_no = String(current_year) + '-' + count
callback(null, rta_no)
}
})
}
For the front-end I'm using pug with simple HTML form.
const rta_no = generateRTA(function (err, res){
if(err){
console.log(err)
}
else{
console.log(res)
}
})
app.get('/new_application', function(req, res){
res.render('new_application', {rta_number: rta_no})
});
I can see the rta_no in console.log but how do I pass it back to the DOM when the value is ready?
Based on the ajax call async response, it will update the div id "div1" when it gets the response from the Node js .
app.js
app.get("/webform", (req, res) => {
res.render("webform", {
title: "Render Web Form"
});
});
app.get("/new_application", (req, res) => {
// Connect to database.
var connection = getMySQLConnection();
connection.connect();
// Do the query to get data.
connection.query('SELECT count(1) as cnt FROM test ', function(err, rows, fields) {
var person;
if (err) {
res.status(500).json({"status_code": 500,"status_message": "internal server error"});
} else {
// Check if the result is found or not
if(rows.length==1) {
res.status(200).json({"count": rows[0].cnt});
} else {
// render not found page
res.status(404).json({"status_code":404, "status_message": "Not found"});
}
}
});
// Close connection
connection.end();
});
webform.pug - Via asynchronous call
html
head
script(src='https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js')
script.
$(document).ready(function(){
$.ajax({url: "/new_application", success: function(result){
$("#div1").html(result.count);
}});
});
body
div
Total count goes here :
#div1
value loading ...
That seems okay, I'm just not sure of this:
The result will be visible before the DOM is generated
This constraint defeats the purpose of async, as your DOM should wait for the returned value to be there. Instead of waiting for it you could just render the page and once the function returns and runs your callback update the value.
Also, perhaps it's worth having a look into promises

Async is calling callback before last item?

I have an async.eachSeries() function which process a list of objects. After the list I want a res.send() function so I can send the result back to the frontend.
But I'm getting a
'Can't set headers after they are sent'
error on the res.send() line, which it's looks that the function is called before the list is completely processed.
module.exports.createOrder = function(req,res){
console.log("CreateOrder");
var orderDescription = "";
var payId = new ObjectId(); //create one id, to pay multiple orders at one time
var shopList = groupByShop(req.body.cart);
var orders = [];
var result = {};
console.log("before async");
//the cart is now sorted by shop, now we can make orders for each shop
async.eachSeries(Object.keys(shopList), function(key, callback){
console.log("in async");
var shop = shopList[key];
console.log("before saveOrder");
saveOrder(payId, shop, key, req.body, req.user, function(err, newOrder){
console.log("in saveorder");
if(err){
console.log("Err", err);
callback(err);
}else{
console.log("order saved");
orders.push(newOrder);
callback();
}
})
}, function(err){
if(err){
console.log("One or more orders are not saved:", err);
return res.status(400).json(err);
}else{
console.log("All orders are processed");
result = {
message: 'OK',
order: {
payId: orders[0].payId
}
};
return res.send(200).json(result);
}
})
}
What is going wrong here? Currently testing with one object in the 'shopList', and all log lines are visible in the server console.
When I remove the line, the function is working fine, but, of course, he is not sending any results. I also tried to move the line outside the function, but that cause, of course again, in a empty result{} and a sending before the function is done.
res.send(200) will send a HTML response with content '200' - what you want to do is res.status(200).json(result) although res.json(result) should also work fine.

Trying to implement callback, or some way to wait until my get request is done. This is in node.js express

Im trying to implement some way to stop my code to redirect me before I get the response from the omdb api I am using.
My function for making a search for a movie and saving all titles in a session looks like this:
app.post('/search', isLoggedIn, function(req, res) {
function getMovies(arg, callback){
console.log('In getMovies');
console.log('searching for '+arg);
omdb.search(arg, function(err, movies) {
if(err) {
return console.error(err);
}
if(movies.length < 1) {
return console.log('No movies were found!');
}
var titles = [];
movies.forEach(function(movie) {
// If title exists in array, dont push.
if(titles.indexOf(movie.title) > -1){
console.log('skipped duplicate title of '+movie.title);
}
else{
titles.push(movie.title);
console.log('pushed '+movie.title);
}
});
// Saves the titles in a session
req.session.titles = titles;
console.log(req.session.titles);
});
// Done with the API request
callback();
}
var title = req.body.title;
getMovies(title, function() {
console.log('Done with API request, redirecting to GET SEARCH');
res.redirect('/search');
});
});
However I dont know if I implement callback in the right way, because I think there can be a problem with the api request actually executing before the callback, but not finishing before. And therefor the callback is working..
So I just want 2 things from this question. Does my callback work? And what can I do if a callback won't solve this problem?
Thankful for all answers in the right direction.
Add
callback();
To, like this
omdb.search(arg, function(err, movies) {
if (err) {
return console.error(err);
}
if (movies.length < 1) {
return console.log('No movies were found!');
}
var titles = [];
movies.forEach(function(movie) {
// If title exists in array, dont push.
if (titles.indexOf(movie.title) > -1) {
console.log('skipped duplicate title of ' + movie.title);
} else {
titles.push(movie.title);
console.log('pushed ' + movie.title);
}
});
// Saves the titles in a session
req.session.titles = titles;
callback();
});
omdb.search is asynchronous function that's why callback executed before omdb.search

Resources