Client side can't fetch server response - node.js

The Problem
I deployed a create-react-app webapp to aws ec2. It's used to display data from a database and send data to it. I use ExpressJS, CORS and MySQL.
With the following code i fetch the corresponding URL and the server.js sends back the database content. Until here, everything works fine.
getBets = _ => {
fetch("http://ec2***.amazonaws.com
.then(response => response.json())
.then(response => this.setState({bets: response.data}))
.catch(err => console.error(err))
};
The problem begins when sending data to the database with the following code:
addBet = _ => {
const { bet } = this.state;
fetch(`http://ec2***.amazonaws.com/bets/add?name=${bet.person_name}&bet=${bet.time_bet}`)
.then(response => response.json())
.then(this.getBets)
.catch(err => console.error(err))
};
On click the addBet-function populates the db, but in chrome I following error:
GET http://ec2***.amazonaws.com/bets/add?name=Peter%20Pan5&bet=10:17%205 net::ERR_EMPTY_RESPONSE
and
TypeError: Failed to fetch
Regarding chrome dev-tools, the first error corresponds to the fetch in the addBet function and the second error to the catch part.
On the server side I've the following code for processing the fetch:
app.get("/bets/add", (req, res) => {
const {name, bet} = req.query;
const INSERT_BET = `INSERT INTO bets (name, bet, timestamp) VALUES("${name}", "${bet}", CURTIME())`;
connection.query(INSERT_BET, (err, res) => {
if (err) {
return res.send(err);
}
else {
return res.send("succesfully added your bet");
}
})
});
I want to mention, that the res paramter in the app.get part is unused. That tells me my IDE.
After a lot of hours digging deeper in the topics of expressJS and the fetch api, I guess, that the app.get part doesn't send a response to the server. But the fetch need some response.
My Question
How do I have to change the code in the app.get part to send a proper response back to the server?
AND
Am I right with my guess?

In MYSQL when you do an insert query you get back err,results and fields in the callback function like this:
connection.query('INSERT INTO posts SET ?', {title: 'test'}, function (error,
results, fields) {
if (error) throw error;
console.log(results.insertId);
});
You have used the parameter res for result and then you have used res.send() which now corresponds to that res parameter in the callback function and not the res object.Rewrite it like this:
app.get("/bets/add", (req, res) => {
const {name, bet} = req.query;
const INSERT_BET = `INSERT INTO bets (name, bet, timestamp) VALUES(?,?,?)`;
connection.query(INSERT_BET,[name,bet,CURTIME()] ,(err, result) => {
if (err) {
return res.send(err);
}
else {
return res.send("succesfully added your bet");
}
})
});
I have also used prepared statement in place of normal sql queries. These are used to prevent sql injections. I hope it will work now.

Related

Node GET by ID API

I have follow the instructions to create a NODE API here.
I'm trying to have a few endpoints with a NODE app to serve data to my React UI.
The database is mongodb where I have a collection for 'stores'.
I have 2 GET calls:
One to retrieve all stores
One to retrieve a store by ID
Node app.js:
app.get('/viewstores', (request, response) => {
storesCollection.find({}).toArray((error, result) => {
if (error) {
return response.status(500).send(error);
}
response.send(result);
});
});
app.get("/viewstores/:id", (request, response) => {
storesCollection.findOne({ "_id": new ObjectId(request.params.id) }, (error, result) => {
if(error) {
return response.status(500).send(error);
}
response.send(result);
});
});
I make my API calls from axios in React.
If I make a call to the first API to retrieve all stores, there no problem at all, but if I try to make the API call by ID, I still get all stores from the first API.
It seems that I am not able to target the GET by ID api.
React app
React.useEffect(() => {
axios.get('http://localhost:5000/viewstores', {
params: { _id: params.storesid}
})
.then(({data}) => {
console.log("DATA ==> ", data)
})
.catch(error => console.log("ERROR API GET ==> ", error))
}, [])
MongoDB store sample:
_id: ObjectId("12345")
businessname:"ABC"
businessaddress:"address abc 1"
Any idea why when I try to call the GET by ID I always get back the whole collection?
Thanks in advance.
Joe.
Assume params.storesid is 12345,
your current React code sends requests to http://localhost:5000/viewstores?_id=12345, and the route /viewstores is reached. To reach the /viewstores/:id route, the URL should be something likes http://localhost:5000/viewstores/12345 then Express will capture the 12345 part in the URL to request.params.id. You can try the code below:
React.useEffect(() => {
axios.get(`http://localhost:5000/viewstores/${params.storesid}`)
.then(({data}) => {
console.log("DATA ==> ", data)
})
.catch(error => console.log("ERROR API GET ==> ", error))
}, [])
You can read about Express route parameters in the official document.

How to fix an endpoint test that returns 404 status code rather than 200 using express, jest and supertest

My end goal is that I want to be able to create a test that satisfies the following statement:
verify that requests to valid URLs return a 200 HTTP status code
A valid URL for example would be /about-page or /jobs, basically any directory that I add in my content folder that contains a file with the extension /index.md.
This is my code so far:
app.js
const readFilePromise = util.promisify(fs.readFile)
app.get('/*', (req, res) => {
readFilePromise(path.join(__dirname, 'content', req.url) + '/index.md', 'utf8')
.then(data => {
convertData(data, res)
})
.catch(err => {
res.status(404).send('Page doesn\'t exist!')
})
})
const convertData = (data, res) => {
const convertedData = md.render(data)
readFilePromise(path.join(__dirname, '/template.html'), 'utf8')
.then(data => {
data = data.replace(/\{\{content\}\}/, convertedData)
res.send(data)
})
.catch(err => {
console.log(err)
})
}
app.listen(3000)
module.exports = app
After reading this article, it mentions that
Requests are asynchronous, which means you must be able to conduct asynchronous tests.
So I wrote the following test:
app.test.js
const app = require('./app.js')
const request = supertest(app)
const supertest = require('supertest')
it('Gets the test endpoint and returns a 200 status', async done => {
const res = await request.get('/*')
expect(res.status).toBe(200)
done()
})
When I run the test, it fails with a 404 status, rather than returning a 200 status. I thought this might be due to my app.js not being in the async/await style, so I changed app.js to:
const readFilePromise = util.promisify(fs.readFile)
app.get('/*', async (req, res) => {
try {
await readFilePromise(path.join(__dirname, 'content', req.url) + '/index.md', 'utf8')
} catch (err) {
res.status(404).send('Page doesn\'t exist!')
}
try {
const convertedData = md.render(data)
await readFilePromise(path.join(__dirname, '/template.html'), 'utf8')
data = data.replace(/\{\{content\}\}/, convertedData)
res.send(data)
} catch (err) {
console.log(err)
}
})
app.listen(3000)
module.exports = app
I tried running the test again, but it still fails with a 404. I think my set up within app.test.js is wrong, but I'm not sure exactly what, as I've tried using the various set ups as the article. How would I fix this?
Separately, when I try going to a URL using the async/await style in app.js, I get a ReferenceError: data is not defined error, but I'm not sure how to define data in the async/await format.
I explained here how to set up app for the test environment: supertest not found error testing express endpoint
You did not mention how you set the database environment, make sure your database is not empty. Then make your get request. but just checking status for get request is not enough because if your db is empty you will still get 200.
const response = await request(app).get("/route").send().expect(200);
expect(response.body.length).toBeGreaterThan(0)
Better approach would be connect to a different database, post your data first and then check the response
const response = await request(app).get("/api/tickets").send().expect(200);
expect(response.body.length).toEqual(2); // if you post two items
Also before you every test make sure you start with empty database inside beforeEach()

Node JS GET API's not working with new data

I am using Node JS and MYSQL. When I add new data to my project, it writes to the database. Then when I want to GET this data with another API, it doesn't come. When I try again after about a minute, it comes on. However, right after I send a request via Swagger, data comes from the outside (Postman or Panel) on my request again.
My simple Controller.
exports.GetAll = (req, res, next) => {
ThisModel.GetAllSQL()
.then((response) => {
res.status(200).json(response[0]);
}).catch((error) => {
res.status(400).send();
console.log('Senaryo listesi çekilirken bir hata meydana geldi: ' + error);
})
}
.then((response) => {
res.status(200).json(response[0]);
})
Judging from the line above, it looks like you're getting a list/array of data, but only returning the first item in the list response[0].
Maybe this is what you're looking for:
.then((response) => {
res.status(200).json(response);
})

Sign up and log in, Express, React, Node, Psql

I am trying to create signup and Login for the first time with express and react using PostgreSQL. My post works just fine. A user can be added to the database so I jumped into handling duplicates.
I am using the findUserByEmail function to find my email and then, in my routes, create the user if it does not exist.
I tried everything and still is giving me problems. I manage to get it working by just returning the query, without a response, which I don't think is right:
const findUserByEmail = (req, response) => {
return pool.query("SELECT * FROM users WHERE email = $1", [req.body.email])
};
Although, I need the response to handle the errors.
The way that I found more common and is how I am trying is:
const findUserByEmail = (req, response) => {
pool.query("SELECT * FROM users WHERE email = $1", [req.body.email]),
(error, results) => {
if (error) {
throw error;
}
response.json(results.rows);
};
};
And when I call it here:
app.post("/signup/user", (req, res, next) => {
queries
.findUserByEmail(req, res)
.then(user => {
if (user.rows.length > 0) {
res.status(400).send("this email is already in use");
} else {
queries.createUser(req.body, res);
}
})
.catch(err => {
console.log(err);
res.status(500).send("Something went wrong");
});
});
But the error is:
Cannot read property 'then' of undefined
If anybody can give me a hand cause I've been 2/3 weeks just for the authentication.
I'll leave my repo if anybody wants to have a look, is a bit messy though.
https://github.com/jaitone/CRUD-in-JS
Thank you!
if you are using pg as part of your project. then:
const findUserByEmail = (req, response) => { // send just email instead
return pool.query("SELECT * FROM users WHERE email = $1", [req.body.email])
};
Is completely legal and beautiful. The library creates a promise and returns it.
I manage to get it working by just returning the query
It is not returning the query, it is returning the mechanism to run the query in a promise wrapper(to be run in the future). So when you do .then it will actually execute and return the result. BUT
If you want to do it manually:
In the findUserByEmail you are not returning a Promise, instead you are just ending the request chain by saying res.json(which in turn means you are returning undefined).
You can create a Promise wrapper or use util.promisfy to make the pool.query a promise.
const findUserByEmail = (req, response) => { // send just email instead
return new Promise((resolve, reject)=>{
pool.query("SELECT * FROM users WHERE email = $1", [req.body.email]),
(error, results) => {
if (error) {
reject(error);
}
resolve(results.rows);
};
});
};
Note, sending the email instead of whole req and res objects is a good idea.

Redirect and Render using Express

When I hit my api I want to redirect my url from https://myapp.herokuapp.com/token/aaa.bbb.ccc to https://myapp.herokuapp.com/messages/:id. I also want to render my message view
Code:
app.get('/token/:id' , (req, res) => {
var decoded = jwt.verify(req.params.id, 'blabla');
Message.findById(decoded.messageId, (err, message) => {
if (err) res.json({error: err})
res.render('message', {message})
})
})
Here, I successfully render my message view but the URL for the below api is still https://myapp.herokuapp.com/token/aaa.bbb.ccc and not https://myapp.herokuapp.com/messages/:id
Another attempt:
app.get('/token/:id' , (req, res) => {
var decoded = jwt.verify(req.params.id, 'blabla');
Message.findById(decoded.messageId, (err, message) => {
if (err) res.json({error: err})
res.redirect('/messages/'+message._id)
})
})
Now, the URL is https://myapp.herokuapp.com/messages/:id but the message view is not rendered. A JSON is rendered that displays the message
How do I redirect to https://myapp.herokuapp.com/messages/:id and also render the message view?
You should first redirect:
app.get('/token/:id' , (req, res) => {
var decoded = jwt.verify(req.params.id, 'blabla');
Message.findById(decoded.messageId, (err, message) => {
if (err) return res.json({error: err}); // see #partycoder's answer
res.redirect('/messages/'+message._id)
})
})
Next, you need to adjust the route handler for /messages/:id. Right now, it sounds like it's only used for XHR requests, so it will always return JSON. You can add a check to see if the request is an XHR-request or not, and either return JSON (for XHR) or a rendered template (for non-XHR):
app.get('/messages/:id', (req, res) => {
...
if (req.xhr) {
return res.json(...);
} else {
return res.render(...);
}
});
(documentation for req.xhr, be aware that the method on which this is based is not foolproof)
However, perhaps it's better to use content negotiation, where the client explicitly tells your server what format the response should be. The upside of this is that it's much more explicit, the downside is that you may have to change some client-side code. Documentation here: http://expressjs.com/en/4x/api.html#res.format

Resources