Issue with Sequelize Query and PUT errors in Chrome - node.js

STUDENT QUESTION!
I'm learning about Node.js/Express and MySQL databases using the Sequelize ORM. Traditionally in our simple applications, after querying a MySQL database with Sequelize, we will issue a res.redirect('/') within Express PUT route's .then promise, similar to this:
app.post("/", function (req, res) {
db.Burgers.create({
burger_name: req.body.burger_name
}).then(function () {
res.redirect('/');
});
});
I'm running into a problem when creating a sequelize query using the findOrCreate() method. Namely, I'm struggling to find where to place the res.redirect statement on an AJAX PUT request. For some reason, when I have the res.redirect('/') attached within the express route for the PUT statement, I will see duplicate PUT requests in the Chrome Network inspector. The first PUT request is displayed as (localhost:3000/devour, type:text/plain, status:302).
The PUT request is received by the Express server and the sequelize query succeeds, updating the proper tables in the MySQL database. However, the redirect on the Express route does not succeed and Chrome Inpector shows an error " PUT http://localhost:3000/ 404 (Not Found)" and when I look at the Network tab I see a second PUT request (localhost:3000/, type:xhr, status:404).
This is the Express PUT route:
app.put("/devour", function (req, res) {
var customerId;
// Check to see if the customer name entered already exists, if not create
db.Customers.findOrCreate({
where: {
customer_name: req.body.customer_name
}
})
.spread((user, created) => {
if (created) console.log("User created");
customerId = user.id;
var update = {
"devoured": req.body.devoured,
"CustomerId": customerId
};
db.Burgers.update(update, {
where: {
id: req.body.id
}
}).then(function () {
res.redirect('/');
});
})
});
What is generating this second PUT request? Is it a response rather than request?
This is my first Sequelize query using the findOrCreate() method so perhaps I'm misunderstading the use of .spread().
If I comment out the res.redirect on the PUT Express route, the error does not occur but I have to manually refresh the page to see the updated data from the MySQL database.
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
I added the 303 status code to my Express PUT route: res.redirect(303, '/'). This elmininated the 404 error on the redirect, however the HTML page was not refreshing with GET request to reload the page with updates from the PUT request.
Then I looked at my Ajax call and realized that, perhaps, I needed to add code there to handle the response from the server:
$.ajax({
url: URL,
type: 'PUT',
contentType: 'application/json',
data: JSON.stringify(dataObject)
})
So I added a .done promise callback and the page successfully refreshes following the PUT request:
$.ajax({
url: URL,
type: 'PUT',
contentType: 'application/json',
data: JSON.stringify(dataObject)
}).done(function(){
window.location.href = window.location.origin + '/'
})
I guess I'm a bit confused about why the server-side res.redirect(303, '/'), alone, doesn't result in the page refresh on the client-side. What is the point of providing the '/' path as an argument?
Thank you!

You can read more about what a redirect header does when included in PUT requests in Why POST redirects to GET and PUT redirects to PUT?
Long story short your PUT request remains a PUT request, just gets redirected to /.
Your app should work properly if you provide 303 status code.
res.redirect( 303, '/' );

Related

Update Frontend after using Delete Route in Express.js

I have created a delete route in express.js that looks like the following:
router.delete("/notes/:id", (req, res) => {
console.log("delete route called")
const noteToRemove = findById(req.params.id, notes);
const result = notes.filter(note => note !== noteToRemove)
console.log(result);
fs.writeFileSync(
path.join(__dirname, '../../db/db.json'),
JSON.stringify({ notes: result }, null, 2)
);
)}
findById is a function I have declared in another file to locate an item in my database db/db.json that locates an item based on an ID I created earlier.
The issue here is that this code will remove an item from the database, but I want the frontend to show the updated list from the database. How does one reload the frontend to see the updated changes in the database?
Two things:
You will need to include a response in your server side method to let the client know that the resource was deleted. You should also add responses with error codes in cases something goes wrong, but for simplicity here we only address the nominal case.
router.delete("/notes/:id", (req, res) => {
console.log("delete route called")
const noteToRemove = findById(req.params.id, notes);
const result = notes.filter(note => note !== noteToRemove)
console.log(result);
fs.writeFileSync(
path.join(__dirname, '../../db/db.json'),
JSON.stringify({ notes: result }, null, 2)
);
res.end(); // respond to the client to let them know we are finished
)}
On the front-end you will need to react to this response and reload the resources. This highly depends on how your front end is structured and how it gets data from the server. As a hack, for now, you can just call location.reload(), which will do the trick in the vast majority of cases -- but isn't very elegant of course nowadays where you could refetch and render just parts of the data and page.

POST http://localhost:3000/login/aa/aa 404 (Not Found)

I have an angular app and a nodejs backend server. I want to get data from my backend but when I try to connect to it with Angular HTTPClient, it says: POST http://localhost:3000/login/aa/aa 404 (Not Found).However, when I put the link manually into the browser, it works perfectly fine. Here is some code:
service.ts
addUser(user: IUser): Observable<IUser> {
return this.httpClient.post<IUser>(`http://localhost:3000/login/${user.email}/${user.passwort}`, user, {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
})
.pipe(catchError(this.handleError));
}
index.js
var mysql = require('mysql');
var express = require('express');
var app = express();
const port = process.env.PORT || 3000;
[...]
app.get('/login/:email/:pw',function(req,res) {
res.setHeader('Content-Type', 'application/json');
var passwort = new Passwort(''+req.params.pw);
passwort.comparePasswort();
con.query("SELECT u.Email, u.Hash FROM User u WHERE u.Email LIKE "+ "'" + req.params.email+ "'", function(err, result ){
if(err) throw err;
console.log(result)
res.send("test")
})
});
Thanks for every answer and for your time!
Your route in your backend is set as a get request and not a post request.
You should either convert your request to a get in your service with this.httpClient.get... or convert to a post request in your backend with app.post.
The reason it works in your browser is that the browser performs a GET request when acessing something using the address bar.
In backed you declared a get method and from frontend you are calling post. your code in service should be :-
addUser(user: IUser): Observable<IUser> {
return this.httpClient.get<IUser>(`http://localhost:3000/login/${user.email}/${user.passwort}`, {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
})
.pipe(catchError(this.handleError));
}
before using /:email you need to subscribe this particular element
const mongoose = require("mongoose");
const User = mongoose.model("User");
const userParams = (req, res, next, email) => {
User.findOne({email:email})
.then((user)=> {
if (!user) {
return res.sendStatus(404);
}
req.user = user;
return next();
})
.catch(next);
};
module.exports = userParams;
then use that in express router by typing
router.param("email", userParams);
this way your router will get to know what the params you are trying to send
In your index.js file, you are creating a handler for a GET request (which is the default request sent by your browser while accessing your webpage)
But in your service.ts file you are trying to send a post request to the server which is not handled, so the simple solution would be to replace the line
return this.httpClient.post<IUser> `http://localhost:3000/login/${user.email}/${user.passwort}`, user, {
with:
return this.httpClient.get<IUser> `http://localhost:3000/login/${user.email}/${user.passwort}`, user, {
For more info you can read this: https://angular.io/guide/http

Cloud Functions and Express req.headers.cookie Undefined

I'm using Firebase Cloud Functions, and the cookie module, and one function sets the "__session" cookie (the only cookie available in Cloud Functions) then executes a "GET" request to another function. In the second function when I try to get the cookie using cookie.parse(req.headers.cookie).__session it says it's undefined. I can see the cookie in the browser however, so I have no idea why I cannot get it in my app. I noticed there are several other SO questions similar to this problem that are also unanswered.
Here is the relevant code from the first function:
const nonce = require('nonce')();
const state = nonce();
res.cookie('__session', state);
request.get('https://myappurl.firebaseapp.com/myapp/path?state=' + state, { json: true })
.catch((error) => {
console.log(error.message);
});
res.redirect('someurl.com');
From the second function:
const cookie = require('cookie');
app.get('/myapp/path', (req, res) => {
console.log(req.headers.cookie); // This is undefined.
const stateCookie = cookie.parse(req.headers.cookie).__session
console.log('stateCookie: ' + stateCookie); // This is undefined.
}
UPDATE
After adding my code, I realized that I'm setting the cookie on res (Express response) but making the get request with request (request-promise module). I'm thinking this may be my issue. I'm going to figure out how to set a cookie to the request and see if that solves it.
I solved this, mostly. Turns out setting the cookie on the Express res was part of the problem. I should be setting it on the request object (request-promise). I tried a couple of ways to set the cookie on the request, including request.cookie. But the only way that worked was this:
var options = {
headers: {
Cookie: '__session=mytestheadercookie'
}
};
request.get('https://myapp.com', options)
.catch((error) => {
console.log(error.message);
});

xhttp GET request to an express.js server - nothing returned

I am trying to do a simple xhttp GET request to an express.js server. Unfortunately I get no response data with this code. The connection is fine as I have successfully used "res.send" to send a body back from the server.
I am not sure if my use of "findOne" on the server is incorrect or if my use of xhttp on the client is incorrect. I suspect it is the client.
I'd appreciate any advice.
* CLIENT CODE *
function getfood() {
var xhttp = new XMLHttpRequest();
xhttp.open("GET", "http://localhost:3000/clientfood", true);
xhttp.send();
}
* SERVER CODE - Express.js / Node *
app.get('/clientfood', cors(), (req, res) => {
//res.send('test'); //this works at least
db.collection('quotes').findOne({
"_id": ObjectId("12345")
},
{
name: 1,
quote: 1
})
})
xhttp GET request to an express.js server - nothing returned
Your server code does not return a response. You need to do something like res.send(...) or res.json(...) to return a response back to the caller and you need to do that in the callback that your database provides for communicating back the result of a query (in most DBs, you can either use a plain callback or a promise).
Your client code does not listen for a response. Example for how to do that shown here on MDN and would typically be:
function getfood() {
var xhttp = new XMLHttpRequest();
xhttp.addEventListener("load", function() {
if (xhttp.status === 200) {
// have data in xhttp.responseText, process it here
} else {
// got some other response here
}
});
xhttp.open("GET", "http://localhost:3000/clientfood", true);
xhttp.send();
}
Thanks so much - especially #jfriend00. I have a lot to learn about how these frameworks work. After taking your advice about SEND I had a little trouble seeing the result on my frontend. I got the message "promise pending". I fixed that with the code suggested in this post.
Express - Promise pending when loop queries
Also I modified my findOne function to grab the entire object for my id.
Final code:
app.get('/clientfood', cors(), (req, res) => {
mydata = db.collection('quotes').findOne(
{
"_id": ObjectId("12345")
})
// promise code
Promise.all([mydata]).then(listOfResults => {
res.send(JSON.stringify(listOfResults)) //for example
}, err => {
res.send(500, JSON.stringify(err)); // for example
});
})

Calling API with POST method via Zombie.js browser

I'm working on testing my node.js code with Zombie.js. I have the following api, which is in POST method:
/api/names
and following code in my test/person.js file:
it('Test Retreiving Names Via Browser', function(done){
this.timeout(10000);
var url = host + "/api/names";
var browser = new zombie.Browser();
browser.visit(url, function(err, _browser, status){
if(browser.error)
{
console.log("Invalid url!!! " + url);
}
else
{
console.log("Valid url!!!" + ". Status " + status);
}
done();
});
});
Now, when I execute the command mocha from my terminal, it gets into browser.error condition. However, if I set my API to get method, it works as expected and gets into Valid Url (else part). I guess this is because of having my API in post method.
PS: I don't have any Form created to execute the queries on button click as I'm developing a back-end for mobile.
Any help on how to execute APIs with POST method would be appreciated.
Zombie is more for interacting with actual webpages, and in the case of post requests actual forms.
For your test use the request module and manually craft the post request yourself
var request = require('request')
var should = require('should')
describe('URL names', function () {
it('Should give error on invalid url', function(done) {
// assume the following url is invalid
var url = 'http://localhost:5000/api/names'
var opts = {
url: url,
method: 'post'
}
request(opts, function (err, res, body) {
// you will need to customize the assertions below based on your server
// if server returns an actual error
should.exist(err)
// maybe you want to check the status code
res.statusCode.should.eql(404, 'wrong status code returned from server')
done()
})
})
it('Should not give error on valid url', function(done) {
// assume the following url is valid
var url = 'http://localhost:5000/api/foo'
var opts = {
url: url,
method: 'post'
}
request(opts, function (err, res, body) {
// you will need to customize the assertions below based on your server
// if server returns an actual error
should.not.exist(err)
// maybe you want to check the status code
res.statusCode.should.eql(200, 'wrong status code returned from server')
done()
})
})
})
For the example code above you will need the request and should modules
npm install --save-dev request should

Resources