does the nextjs api honor promises? - javascript

I hope you are well, I have a problem, I am learning NEXTJS and working with its api, and it turns out that when I click very fast the promises get bugged. Or something similar, I leave the tests in this short video of 30s and a demonstration of the code, I hope someone can help me, thank you very much.
https://www.youtube.com/embed/K0JTMxpFLQs
------------ CODE ----------
MY REQUEST AXIOS:
axios.post("/api/reaction", data).then((response) => { return response.data }).then(response => {
if (response.react) {
setNew[key].youLike = true
setNew[key].likes = response.likes
setting(setNew => [...setNew])
} else {
setNew[key].youLike = false
setNew[key].likes = response.likes
setting(setNew => [...setNew])
}
})
My backend:
export default async function react(req, res) {
let msg, msg_type, react, likes
let { type, id } = req.body
let myId = mySession(req)
if (myId && type && id) {
react = await youLike(myId, id, type)
console.log(react)
if (react) {
await executeQuery({ query: `DELETE FROM cms_likes WHERE owner_id='${myId}' and type='${type}' and post_id='${id}'` })
await executeQuery({ query: `UPDATE cms_posts SET likes=likes-1 WHERE id='${id}' ` })
react = false
} else {
await executeQuery({ query: `INSERT INTO cms_likes (owner_id,type,post_id,time) VALUES ('${myId}','${type}','${id}','${timeNow("s")}') ` })
await executeQuery({ query: `UPDATE cms_posts SET likes=likes+1 WHERE id='${id}' ` })
react = true
}
likes = await executeQuery({ query: `SELECT likes FROM cms_posts WHERE id='${id}'` })
} else {
msg_type = "error"
msg = "!Opps error al enviar tu peticion"
}
res.send({ msg, msg_type, react, likes: likes[0]?.likes })
}
By clicking the button many times, the like button doesn't do the promise to check if it exists or not very well, and doubles the likes.
EXISTS SLOW CLIC
slowclick
response:
0 no found
1 Yes there are
0 no found
1 Yes there are
0 no found
FAST CLIC RESPONSE:
response
2 DUPLICATE
2 DUPLICATE
0 no found
1 Yes there are
0 no found
1 Yes there are
0 no found
3 duplicate
Understand how promises work well, in order to find a solution to this problem, and my post will help people who may have the same error.
My connection SQL and FUNCTION EXECUTEQUERY
const db = mysql({
config: {
host: "localhost",
database: "baalbcms",
user: "root",
password: ""
}
})
export default async function executeQuery({ query, values }) {
try {
const results = await db.query(query, values)
await db.end()
return results
} catch (err) {
return err;
}
}

To make your SQL more robust, you should always count the actual number of likes based on the likes table; as it is, it's possible for those to get out of sync.
I also took the liberty of fixing the SQL injection vulnerabilities in your code by using values.
This would be better still if you ran the queries in a single transaction, but that can't be easily done with your current db that you end after every query (which you shouldn't do).
if (react) {
await executeQuery({
query: "DELETE FROM cms_likes WHERE owner_id=? and type=? and post_id=?",
values: [myId, type, id],
});
} else {
await executeQuery({
query: "INSERT INTO cms_likes (owner_id,type,post_id,time) VALUES (?, ?, ?, ?)",
values: [myId, type, id, timeNow("s")],
});
}
await executeQuery({
query: "UPDATE cms_posts SET likes=(SELECT COUNT(*) FROM cms_likes WHERE type=? and post_id=?) WHERE id=?",
values: [type, id, id],
});
likes = await executeQuery({ query: `SELECT likes FROM cms_posts WHERE id=?`, values: [id] });

Related

how to toggle mongodb field value on a click

I am working on an e-commerce project. admin wants to change the user status on a click I tried a way to update the status of the user
<td id="status" onclick="changeStatus('<%=user._id%>' , '<%=user.status%>')"></td>
async function changeStatus(id , status){
try{
const res = await fetch("/admin/customers" , {
method:"POST",
body:JSON.stringify({id , status}),
headers:{"Content-Type":"application/json"}
})
const data = await res.json()
if(data.message.includes("success")){
location.reload()
}
}
catch(err){
console.log(err)
}
}
and in my serverside i tried update my status
customer_status: async (req , res) => {
let {id, status} = req.body
try{
const updateStatus = await User.findByIdAndUpdate({_id:id} , {$set:{status:!status}} , {new:true})
console.log(updateStatus)
res.json({message:"success"})
}
catch(err){
console.log(err)
}
},
It works for the first time for a user anyone can explain why it's not updating simontionously
We should use $not aggregation for toggling boolean:
User.findByIdAndUpdate({ _id: id}, { $set: { status: { $not: "$status" }}}, { new: true })
Also note that some value cannot be casted to true or false, eg empty string ''. In this case we have to tell Mongo how to handle, below snippet is for mongoose:
const mongoose = require('mongoose');
mongoose.Schema.Types.Boolean.convertToFalse.add('');

How can I use either req.query, or req.params, or req.* outside its scope without saving in database

I've been trying to build a helper function that will allow me to apply DRY pattern in order to stop repeating myself. My backend and the whole application is complete, however, I'd like to optimize my code, and my actual problem is that pretty much every express http method is similarly formatted the same way. The only time I've happened to come close to the solution is when I've omitted the req.params as arguments. The problem is that each method has its own req.params format. Here's how I was trying to solve:
I tried to use node-persist package to store req.params, and it only works after I change and resave the file, which makes sense. This happened as I first passed the params as the argument and tried to pass the persisted params value when I call the function. If there's a way to have the req.params stored somewhere locally first, I wouldn't be worried.
Second Option, I tried to use recursion and called the so-called function twice. I expected the first call to return an undefined params, and the second function call to return stored req.params, but unfortunately it wasn't the case.
I then decided to try using req.redirect where I've to access the database with req.params that came from req.query. Although this works, it still brings me back to the same problem as I'll keep redirecting everything
My problem, I want to have a helper function like the following:
export const storage = require('node-persist'); //package to persist data
Few of types used:
type AllHTTPMethods = "post" | "delete" | "all" | "get" | "put" | "patch" | "options" | "head";
type HTTPMethod = core.IRouterMatcher<core.Express, AllHTTPMethods>;
export async function onFetch(httpMethod: HTTPMethod | any, sql: string, path:string, params?: string){
httpMethod(path, async(req, res) => {
await storage.init({});
/**
Check if there is something already stored
*/
if (Object.keys(req.params).length === 0) {
await storage.setItem('params', req.params)
await storage.updateItem('params', req.params)
}
conn.query(sql, [params],
(err:any, data:any) => {
if (err) {
return new Error("Something went wrong\n"+err)
}
console.log("Successfully fetched");
console.log(data)
return res.json(data);
})
})
}
Here's how I invoked them:
//This one works because params aren't involved
async all() {
await onFetch(app.get.bind(app), "select * from products", "/products")
.then(() => console.log("Successfully fetched products"))
.catch(e => console.error(e))
}
//This one didn't work Because the function needs to called first before
//persisting
getProductById = async () => {
await storage.init()
const paramsObj = await storage.getItem("params"); //returns empty object {}
await onFetch(app.post.bind(app), "select * from products where id = ?", "/product/:id", paramsObj.id)
}
And the last trick I tried was to have req.params from client upfront, then redirect them to another router
Helper function to send req.params:
export function generateParams(
httpMethod: HTTPMethod,
path: string,
params: string,
) {
httpMethod(path, (req, res) => {
const paramsObject = JSON.stringify(req.params);
return res.redirect(`/params/api/?${params}=${paramsObject}`)
})
}
Calling:
generateParams(app.post.bind(app), "/product/:id", "product")
It works but it's still the same problem I was trying to avoid beforehand
app.get('/params/api', async (req, res)=> {
var product: string | string[] | any | undefined = req.query.product;
var id:any = JSON.parse(product).id
conn.query("select * from products where id = ?", [id], (err, data)=>{
if (err) {
return
}
res.json(data)
})
});
Thanks in advance
I created a helper function to handle the queries and params based on the current req.route.path, then return an array of string containing those queries
function setRequestData(request: any) {
var arr: any[] = [];
const query = request.query
const path = request.route.path
if (path === '/product/:id') {
arr = [...arr, request.params.id]
}
else if (path === '/add/user') {
arr = [...arr,
query.firstName,
query.lastName,
query.email,
query.phoneNumber,
query.passCode,
query.age,
]
}
console.log(arr)
return arr
}
Then I used it as the following:
export function onFetch(httpMethod: HTTPMethod, sql: string | mysql.QueryOptions, path:string){
try {
httpMethod(path, (req, res) => {
var queries:any[] = setRequestData(req)
conn.query(sql, queries, (err:any, data:any) => {
if (err) {
return new Error("Something went wrong\n"+err)
}
console.log("Successful request");
res.json(data);
})
})
} catch (error) {
console.error(error)
}
}
Now, I'd be just calling one line of code to communicate with mysql database no matter which method I intend to use:
var sql = `insert into Users(firstName, lastName, email, phoneNumber, passCode, age, joined) values(?, ?, ?, ?, ?, ?, current_timestamp())`;
onFetch(app.post.bind(app), sql, '/add/user')

Creating a profile in mySQL database and using it right after to display what's inside it Discord.js

I'm creating my Discord bot, and I saw that with the hosting provider I bought came with a mySQL database, so I'm using it. I connected to it with the mySQL NPM package:
export const con = mysql.createConnection({
host: process.env.MYSQL_HOST,
port: process.env.MYSQL_PORT,
user: process.env.MYSQL_USER,
password: process.env.MYSQL_PASSWORD,
database: process.env.MYSQL_DB,
});
and it works fine. I created a table in the database, with 3 parameters:
id: Discord user id
bananas: My toy currency
deposit: The currency deposit
I set it up that on the interactionCreate event, whenever a user uses an interaction, it checks the database, if there is a profile, it does nothing, else, it creates the profile in the database. Code:
async checkDB(int: CommandInteraction | MessageComponentInteraction) {
await con.query(
`SELECT * FROM profileSchema WHERE id = '${int.user.id}'`,
async (e: Error, rows: any) => {
if (e) throw e;
if (!rows[0]) {
await con.query(
`INSERT INTO profileSchema (id, bananas, deposit) VALUES ('${int.user.id}', 100, 0)`
);
}
}
);
},
this code works fine, the problem is that if a user does not have a profile in the database, and they use a currency related command, like the one that shows their balance, the bot crashes because their credits in the database result nonexistent, even though the profile gets created and the second time they use the command it works properly! How can I code so that if the user is not in the database and uses a command, it creates and displays at the same time? Here's the code that checks the currency balance:
await con.query(
`SELECT * FROM profileSchema WHERE id = ${interaction.user.id}`,
async (e: Error, rows: any[]) => {
if (e) throw e;
let wallet: {
bananas: number;
deposit: number;
};
try {
wallet = {
bananas: rows[0].bananas,
deposit: rows[0].deposit,
};
} catch (e) {
throw e
return;
}
)
So unless it is really needed, you don 't need the async/await components in this but the simple answer you are looking for is to change if (!rows[0]) to if (!rows.length) but that works when you set up your query like such:
con.query(`SELECT * FROM profileSchema WHERE id = '${int.user.id}'`, (err, rows) => {
if (err) throw err;
if (!rows.length) {
con.query(`INSERT INTO profileSchema (id, bananas, deposit) VALUES ('${int.user.id}', 100, 0)`, (err) => {
if (err) throw err;
});
} else {
let wallet = {
bananas: rows[0].bananas,
deposit: rows[0].deposit,
}
}
});

Mongoose updateOne() going through okay but not updating

I have this request:
// PUT that updates a user.
router.put('/api/user/:id', async (req: Request, res: Response) => {
const { email, name, avatar } = req.body
const userId = req.body._id
const conditions = {
_id : userId
}
const user = {$set: { "email": email, "name": name, "avatar": avatar } }
User.updateOne(conditions, user).then(doc => {
if (!doc) { return res.status(404).end() }
return res.status(200).json(doc)
}).catch(error => console.log(error))
})
And I get this response from the request:
{
"n": 0,
"nModified": 0,
"ok": 1
}
If you can find it on StackOverflow about the updateOne() method in mongoose I've probably tried it. The document isn't updating no matter what I try.
Edit: I've tried using an ObjectID in the query instead and the same result.
Edit 2: I figured it out. Was using req.body.id instead of req.params.id and I was using parameters to send the request. Thanks everyone for the help!
nModified == 0 implies that you have no user matching this id,
your route is put /api/user/:id but your user id is in req.params.id and not in req.body._id
A couple tips:
Try running the same query from mongodb at the command line, see if you get any results.
Is the "campaign_id" defined as an ObjectId in your schema? If so, try searching using the ObjectId type.
Try to change the query to :
const ObjectId = require('mongoose').Types.ObjectId;
const conditions = {
_id : new ObjectId(userId)
}
The reason for not updating is - mongoose is unable to search the with the id you provided.
if you want to update a document based on _id you can use findByIdAndUpdate()
const userId = req.body._id;
const user = { "email": email, "name": name, "avatar": avatar }
User.findByIdAndUpdate(userId , user,
function (err, docs) {
if (err){
console.log(err)
}
else{
console.log("Updated User : ", docs);
}
});
In case you've set your DB to strict mode don't forget to add strict:false in options when adding new keys. Otherwise, inserts will be silently ignored. I've just spent 2 hours wondering why my inserts don't get saved in DB despite not throwing any error.
See dos
http://mongoosejs.com/docs/guide.html#strict
const conditions = {
_id
}
const dateToUpdate = {
$set: {
"email": "email",
"name": "name",
"avatar": "avatar"
}
}
const updateRecord = await models.pdDealModel.updateOne(conditions,dateToUpdate,{
upsert:false,
strict:false
}
)

Unhandled Promise Rejection Error in node js

am getting error while executing combined queries
Am using, sequelize and mysql
Here is my code:
const makeAuthenticate = async (req, res) => {
const newOtp = Math.floor(100000 + Math.random() * 900000);
try {
const {phone} = req.body;
const [user, created] = await db.User.findOrCreate({
where: { phone: phone },
})
.then(e => {
db.User.update({ otp: newOtp }, {
where: {
phone: phone
}})
.then(f => res.status(200).json({
otp: newOtp
}))
.catch(err => res.status(500).json({error: err})) // Error comes from here !
})
.catch(err => res.status(500).json({error: err}))
} catch (error) {
return res.status(500).json({error: error.message})
}
}
And here is what am doing with the code:
Basically, am doing both signup and signin in single query, in other words at first using findOrCreate - i find the user or i will create a new user, and then i need to send one time password (which is temp 6 digit numerics") to the phone number which they use on the process.
So, am updating the otp to my users table and then i need to add up the call to send SMS to the user ( i havent done that yet ) so as of now am at the stage of findOrCreate and make another update call to the specific user.
By this am getting an error as follows:
UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
Try something like this
const makeAuthenticate = async (req, res) => {
const newOtp = Math.floor(100000 + Math.random() * 900000)
const {phone} = req.body
try {
const [user, created] = await db.User.findOrCreate({
where: { phone: phone }
})
const updatedRes = await db.User.update({ otp: newOtp }, { where: { phone: phone }})
res.status(200).json({
otp: newOtp
})
} catch(e) {
res.status(500).json({error: err})
}
}
There is no need to nest promises and .then() you can just use await since Sequelize uses promises.
The main issue with your code is that you use .catch() inside try catchand they both are trying to send a status on failure. So most likely your issue is somewhere else but this was a side effect, when you run my code you should see your real issue if it exist.

Categories

Resources