I am trying to Increase a value in database,
const handleStockUpdate = (event) => {
event.preventDefault();
const newQuantity = event.target.restock.value;
const quantity = {quantity: newQuantity};
setNewCount({...book, quantity: book.quantity + parseInt(newQuantity)});
if(newQuantity < 0){
toast("Please enter a vlaid number")
}
else{
axios.put(`http://localhost:5000/stock/${`idHeare`}`, {quantity})
}
}
api
app.put('/stock/:id', async(req, res) => {
const id = req.params.id;
console.log(id);
const qunatity = req.body.quantity.quantity;
const book = await bookCollections.findOne({_id: ObjectId(id)});
const newUpdate = parseInt(book.quantity) + parseInt(qunatity);
const result = await bookCollections.updateOne({_id:ObjectId(id)},
$set({qunatity: newUpdate})
)
res.send(result);
})
My question is, why should I use Id? and where can I get it? without Id it gets networkErrro
Put method is used to update data.
The id you are providing to the api will be used to retrieved that row from your database which you want update. As you may know that in a database table every data row must have a unique id.
So for your book object , if you are retrieving it from the database in the first place then you should have a Id with that book data you got.
You have to provide that book id on the put request.
Related
I have an old project I built with node js and MongoDB, it is a fintech application. I recently noticed that I can trick the system by trying to withdraw the same value twice using two separate clients at the same time.
For instance, I have a balance of 10K units, Whenever I try a withdrawal of 10k on two different clients at the same time, both requests end up successful. I believe this is happening because as at the processing of both requests, the user still has a balance of 10K, therefore, making both requests valid
I have been searching the internet for a possible solution to this but I can't seem to find anything useful (I basically don't know the right keyword to search). Is there a way can block other requests from being processed on the server or DB until the other first one resolves?
This is what my code looks like
//Code checks if the account number is valid
const accountValidation = await handleBankAccountVerification(account_no, bank);
if(accountValidation.status === 'error' || !accountValidation.data) return res.status(400).send(accountValidation);
//Check if balance is sufficient
const user = await User.findById(req.user._id)
if(user.balance < amount) return res.status(400).send({message:"Insufficient balance!"})
const payWithManual = async (user, account_name) =>{
try{
//Update User DB balance
const task = new Fawn.Task()
const data = req.body
data.status = "pending"
data.balance = user.balance
data.account_name = account_name || ''
data.user = req.user._id
const {error} = validatePayment(data);
if(error) return res.status(400).send({message:error.details[0].message})
const result = await task.update("users",{_id:req.user._id}, {balance:user.balance - amount}) //result will be {_id: someMongoId, name: {full: "Brody Obi"}}
.save("payments", data )
.save("transactions", {
...transaction,
action_id:{$ojFuture: "1._id"},
balance_before:user.balance,
balance_after:user.balance - amount,
})
.run({useMongoose: true})
const date = dayjs()
const payload = {
"country":"NG"
}
const response = await flw.Bank.country(payload)
bankName = response.data.filter(item =>{
return item.code === bank
})
const html = WithdrawTemplate(amount,user.balance - amount,date.format("hh:mmA MMM DD, YYYY"),account_no, response ? bankName[0].name : null )
const adminHtml = WithdrawNotificationTemplate(user.email,amount,user.balance - amount,date.format("hh:mmA MMM DD, YYYY"),account_no, response ? bankName[0].name : null )
const email = await user.sendEmailToUser(`Your made a withdraw`, html, user.email)
const emailAdmin = await user.sendEmailToUser(`A user is requesting withdrawal`, adminHtml, config.get('NOTIFICATION_EMAIL'))
return res.status(200).send({message:"Fund has been sent to account you provided!", result:result[1]._id})
return res.status(200).send({message:"Success"})
}
catch(e){
res.status(400).send(e)
}
}
const account_name = accountValidation.data.data.account_name;
await payWithManual(user, account_name)
I am running on nodejs version v14.17.6 and mongoose v5.9.9
I'm trying to get the updated number for a Member Number from a document using Cloud Functions when an admin creates a user.
What should happen is when an Admin creates a user in their dashboard, the user is added to firebase, then the member number updates, and is applied to newMemNum, then updated in the user document.
My Code:
const memNumInc = admin.firestore.FieldValue.increment(1);
const memCounter = admin.firestore().collection(`mem_num`).doc(`a1lDbrsoXjfKeosEkDtw`);
memCounter.update({number: memNumInc}).then(() =>
memCounter.get()
.then((snap) => {
const id = snap.id;
const data = snap.data()
newMemNum = data['number']
console.log('New Member Number: ' + newMemNum);
return {id, ...data};
})
);
The increment goes ok (i.e. number goes up by 1 which is expected), but the next part of the code doesn't run and doesn't throw an error.
Also, the next part of the code that updates the user document doesn't fire at all, and no errors.
Entire Code:
// Create User Document onCreate
const createProfile = (userRecord) => {
const uid = userRecord.uid;
const docId = userRecord.uid;
const fullName = userRecord.displayName || 'New User';
const memStatus = 'Unfinancial';
const isAdmin = false;
const isNew = true;
const email = userRecord.email;
const photoUrl = userRecord.photoUrl;
const phone = '0444 123 456';
const createdAt = admin.firestore.FieldValue.serverTimestamp();
const memNumInc = admin.firestore.FieldValue.increment(1);
const memCounter = admin.firestore().collection(`mem_num`).doc(`a1lDbrsoXjfKeosEkDtw`);
memCounter.update({number: memNumInc}).then(() =>
memCounter.get()
.then((snap) => {
const id = snap.id;
const data = snap.data()
newMemNum = data['number']
console.log('New Member Number: ' + newMemNum);
return {id, ...data};
})
);
return afs
.collection(`users`)
.doc(docId)
.set({
uid: uid,
docId: docId,
fullName: fullName,
joined: createdAt,
memNum: newMemNum,
memStatus: memStatus,
isAdmin: isAdmin,
isNew: isNew,
email: email,
photoUrl: photoUrl,
phone: phone,
addedOn: createdAt,
updatedOn: createdAt
})
.then(() => console.log('User Creaeted Successfuly: ' + uid))
.catch((e) => console.log('User Creation Error: ' + e.message));
}
exports.authOnCreate = functions.auth.user().onCreate(createProfile);
If I remove the memCounter code, the rest executes no problem.
You have another return statement that most likely runs before the promise returned by get() is resolved. Try refactoring the code using async-await syntax as shown below:
const createProfile = async (userRecord) => {
// const vars ....
const memNumInc = admin.firestore.FieldValue.increment(1);
const memCounter = admin.firestore().collection(`mem_num`).doc(`a1lDbrsoXjfKeosEkDtw`);
// Update documents
await memCounter.update({
number: memNumInc
})
// Get update document data
const snap = await memCounter.get()
const id = snap.id;
const data = snap.data()
newMemNum = data['number']
console.log('New Member Number: ' + newMemNum);
return afs
.collection(`users`)
.doc(docId)
.set({ ... })
}
However, if multiple users are created simultaneously, there's a chance that they'll get the same newMemNum so a transaction might be useful as well.
Firestore also introduced a new COUNT() function that can be used to get total number of documents in a collection instead of incrementing the count every time.
I want to increment the value of the field "votes" in a document (item_id) in the collection items. I want a cloud function to do this for me every time a new document is added to the collection votes. The new document contains the item_id. does anyone know how I can do this? This is what I have now:
import * as functions from "firebase-functions";
import * as admin from "firebase-admin";
admin.initializeApp();
export const vote = functions.firestore.document("/Votes/{vote}")
.onCreate((snapshot, context) => {
const item = context.params.item_id;
const itemdoc = admin.firestore().collection("items").doc(item);
itemdoc.get().then((doc) => {
if (doc.exists) {
itemdoc.update({
"votes": admin.firestore.FieldValue.increment(1)})
.catch((err) => {
console.log("Error updating item vote", err);
});
}
});
});
In the firebase console logs that the path must be a non-empty string. Does anyone know what I do wrong? Since the path should not be empty.
The following should do the trick:
export const vote = functions.firestore.document("/Votes/{vote}")
.onCreate((snapshot, context) => {
const item = snapshot.data().item_id;
const itemDocRef = admin.firestore().collection("items").doc(item);
return itemDocRef.update({
"votes": admin.firestore.FieldValue.increment(1)
});
});
You need to use the data() method on snapshot, in order to get the JavaScript representation of the new document. Then you take the item_id property.
Another possibility is to use the get() method, as follows:
const item = snapshot.get("item_id");
I would suggest to rename the itemdoc variable to itemDocRef, since it is a DocumentReference.
Update following your comments:
If you want to read the item Doc after having updated it you should do as follows:
export const vote = functions.firestore.document("/Votes/{vote}")
.onCreate(async (snapshot, context) => {
const item = snapshot.data().item_id;
const itemDocRef = admin.firestore().collection("items").doc(item);
await itemDocRef.update({"votes": admin.firestore.FieldValue.increment(1)});
const itemDocSnapshot = await itemDocRef.get();
//Do whatever you want with the Snapshot
console.log(itemDocSnapshot.get("user_id"));
// For example update another doc
const anotherDocRef = admin.firestore().collection("....").doc("....");
await anotherDocRef.update({"user_id": itemDocSnapshot.get("user_id")});
return null;
});
Note the use of the async and await keywords.
const item = context.params.item_id;
By accessing context.params, you are trying to find a value in wildcard present in .document("/Votes/{vote}") which is undefined for sure. To read a field from document try this:
const {item_id} = snapshot.data()
// Getting item_id using Object destructuring
if (!item_id) {
// item_id is missing in document
return null
}
const itemdoc = admin.firestore().collection("items").doc(item_id);
// Pass item_id in doc ^^^^^^^
You can read more about onCreate in the documentation. The first parameter snapshot is the QueryDocumentSnapshot which contains your doc data and the second parameter context is EventContext.
I am creating a cloud firestore function. The last step I need for now is using the userId to retrieve a document.
Here I get the userId
const userId = snap.data().userId; <<< THIS WORKS
console.log('A new transaction has been added');
Here I want insert the value from userId to retrieve the correct document.
const deviceDoc = db.collection('device').doc(**userId**); <<< THIS IS THE PROBLEM
const deviceData = await deviceDoc.get();
const deviceToken = deviceData.data().token;
I don't know how to use the variable, userId, to insert the value into the .doc(userId) to get the data.
If userId = 12345 I want the line to look like this:
const deviceDoc = db.collection('device').doc('12345');
I have tried .doc('userId'), .doc('${userId}'), as well as other things. None of these work.
How do I do this?
As Puf has responded, you can simply use doc(userId). The rest of your code looks fine, so maybe the document you are getting doesn't exist. Try the following:
const deviceRef = db.collection('device').doc(userId);
// you can shorten this to >> const deviceRef = db.doc(`device/${userId}`);
try {
const deviceDoc = await deviceRef.get();
if (!deviceDoc.exists) {
console.log(`The document for user ${userID} does not exist`);
} else {
const {token} = deviceDoc.data();
console.log('The token is:', token);
}
}
catch (err) {
console.error(err);
}
REVISED QUESTION
I've revised the question, in the hope of getting a clearer answer.
I'm trying to process data in ExpressJS, based on the incoming req.body and the existing data in the table.
I'm receiving a req.body that contains a JSON list of updated fields. Some of those fields are stored as JSONB in Postgres. If an incoming field is JSONB, then the form (external code) that is making the request has already run a jsonpatch.compare() to generate the list of patches, and it is these patches and not the full values that are being passed in. For any non-JSONB values, incoming values just need to be passed through to the UPDATE query.
I have a working version, as below, that pretends that the existing JSONB values in the table ARE NULL. Clearly, this is NOT what is needed. I need to pull the values from the db. The non-querying-of-current-values version and a bare minimum router, looks like this:
const express = require('express')
const bodyParser = require('body-parser')
const SQL = require('sql-template-strings')
const { Client } = require('pg')
const dbConfig = require('../db')
const jsonpatch = require('fast-json-patch')
const FormRouter = express.Router()
I have some update code:
````javascript
const patchFormsRoute = (req, res) => {
const client = new Client(dbConfig)
const { id } = req.body
const parts = []
const params = [id]
// list of JSONB fields for the 'forms' table
const jsonFields = [
'sections',
'editors',
'descriptions',
]
// list of all fields, including JSONB fields in the 'forms' table
const possibleFields = [
'status',
'version',
'detail',
'materials',
...jsonFields,
]
// this is a DUMMY RECORD instead of the result of a client.query
let currentRecord = { 'sections':[], 'editors':[], 'descriptions':[] }
possibleFields.forEach(myProp => {
if (req.body[myProp] != undefined) {
parts.push(`${myProp} = $${params.length + 1}`)
if (jsonFields.indexOf(myProp) > -1) {
val = currentRecord[myProp]
jsonpatch.applyPatch(val, req.body[myProp])
params.push(JSON.stringify(val))
} else {
params.push(req.body[myProp])
}
}
})
const updateQuery = 'UPDATE forms SET ' + parts.join(', ') + ' WHERE id = $1'
client.connect()
return client
.query(updateQuery, params)
.then(result => res.status(200).json(result.rowCount))
.catch(err => res.status(400).json(err.severity))
.then(() => client.end())
}
FormRouter.route('/')
.patch(bodyParser.json({ limit: '50mb' }), patchFormsRoute)
exports.FormRouter = FormRouter
I promise, that this is working code, which does almost what I need. However, I want to replace the dummy record with the data already in the table, fetched contemporaneously. My issue, is because multiple clients could be updating a row at the same time (but looking at orthogonal elements of the JSONB values), I need the fetch, calc, and update to happen as a SINGLE TRANSACTIOn. My plan is to:
BEGIN a transaction
Query Postgres for the current row value, based on the incoming id
For any JSONB fields, apply the patch to generate the correct value for that field in the UPDATE statement.
Run the UPDATE statement with the appropriate param values (either from the req.body or the patched row, depending on whether the field is JSONB or not)
COMMIT the transaction, or ROLLBACK on error.
I've tried implementing the answer from #midrizi; maybe it's just me, but the combination of awaits and plain testing of res sends the server off into Hyperspace... and ends in a timeout.
In case anyone is still awake, here's a working solution to my issue.
TLDR; RTFM: A pooled client with async/await minus the pooling (for now).
const patchFormsRoute = (req, res) => {
const { id } = req.body
// list of JSONB fields for the 'forms' table
const jsonFields = [
'sections',
'editors',
'descriptions',
]
// list of all fields, including JSONB fields in the 'forms' table
const possibleFields = [
'status',
'version',
'detail',
'materials',
...jsonFields,
]
const parts = []
const params = [id]
;(async () => {
const client = await new Client(dbConfig)
await client.connect()
try {
// begin a transaction
await client.query('BEGIN')
// get the current form data from DB
const fetchResult = await client.query(
SQL`SELECT * FROM forms WHERE id = ${id}`,
)
if (fetchResult.rowCount === 0) {
res.status(400).json(0)
await client.query('ROLLBACK')
} else {
const currentRecord = fetchResult.rows[0]
// patch JSONB values or update non-JSONB values
let val = []
possibleFields.forEach(myProp => {
if (req.body[myProp] != undefined) {
parts.push(`${myProp} = $${params.length + 1}`)
if (jsonFields.indexOf(myProp) > -1) {
val = currentRecord[myProp]
jsonpatch.applyPatch(val, req.body[myProp])
params.push(JSON.stringify(val))
} else {
params.push(req.body[myProp])
}
}
})
const updateQuery =
'UPDATE forms SET ' + parts.join(', ') + ' WHERE id = $1'
// update record in DB
const result = await client.query(updateQuery, params)
// commit transaction
await client.query('COMMIT')
res.status(200).json(result.rowCount)
}
} catch (err) {
await client.query('ROLLBACK')
res.status(400).json(err.severity)
throw err
} finally {
client.end()
}
})().catch(err => console.error(err.stack))
}