Pass variable into collection(collectionName).doc(docName) - node.js

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);
}

Related

Get data from firestore document and use in cloud function

In the user's collection, each user has a document with a customer_id.
I would like to retrieve this customer_id and use it to create a setup intent.
The following code has worked for me in the past. However, all of a sudden it throws the error:
Object is possibly 'undefined'
The error is on the following line under snapshot.data() in this line:
const customerId = snapshot.data().customer_id;
Here is the entire code snippet:
exports.createSetupIntent = functions.https.onCall(async (data, context) => {
const userId = data.userId;
const snapshot = await db
.collection("development")
.doc("development")
.collection("users")
.doc(userId).get();
const customerId = snapshot.data().customer_id;
const setupIntent = await stripe.setupIntents.create({
customer: customerId,
});
const clientSecret = setupIntent.client_secret;
const intentId = setupIntent.id;
return {
clientsecret: clientSecret,
intentId: intentId,
};
});
Any help is appreciated :)
this is because snapshot.data() may return undefined
there are 2 ways to solve this
first is assert as non-null, if you have high confident that the data exist
const customerId = snapshot.data()!.customer_id;
second if check for undefined
const customerId = snapshot.data()?.customer_id;
if(customerId){
// ....
}
I recommend the 2nd method, it is safer
I can see you are using a sub collection order,You need to loop through the snapshot data using the forEach loop.
const customerId = snapshot.data()
customerId.forEach((id)=> {
console.log(id.customer_id)
});
Try this out but.
The document you're trying to load may not exist, in which case calling data() on the snapshot will return null, and thus this line would give an error:
const customerId = snapshot.data().customer_id;
The solution is to check whether the document you loaded exists, and only then force to get the data from it:
if (snapshot.exists()) {
const customerId = snapshot.data()!.customer_id;
...
}
if you want to fetch user data from docId then you can use something like this:
const functions = require("firebase-functions");
var admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
var db = admin.firestore();
db.settings({ timestampsInSnapshots: true });
exports.demoFunction = functions.https.onRequest((request, response) => {
var userId = request.body.userId;
db.collection("user").doc(userId).get().then(snapshot => {
if (snapshot) {
var data = snapshot.data();
// use data to get firestore data
var yourWantedData = data.name;
// use it in your functionality
}
});
});

Why do we use put id as a param in put method

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.

What is the proper way to update a particular column in nestjs

I want to save token generated into the user's confirmed email column. The token is part of the confirmation link that will be sent to the user so that when the user clicks on the link I can check if it matches, then updates it to "activated".
Now the problem is I can't figure out how to save it in the ConfirmEmailLink method .
async register(createDTO: CreateUserDto) {
const { email } = createDTO;
const user = await this.userModel.findOne({ email })
if (user) {
throw new HttpException('User already exists', HttpStatus.BAD_REQUEST);
}
const createdUser = new this.userModel(createDTO);
var newUser = await createdUser.save();
await SendEmail(createDTO.email, await **this.ConfirmEmailLink(createdUser._id)**, createDTO.email);
return this.sanitizeUser(createdUser);
//return null;
}
In the above code there is ConfirmEmailLink that is a parameter to SendEmail method
async ConfirmEmailLink(userId: string) {
const id = v4();
var payload = { userId: userId };
var secret = process.env.JWT_SIMPLE_TOKEN;
var token = jwt.encode(payload, secret);
console.log("This is uuid", userId);
var link = `${process.env.HOST}/user/confirm/${token}/${id}`;
let user = await this.userModel.findById(userId);
if (!user) {
throw new HttpException("Registration not complete, try again or contact admin", HttpStatus.NOT_FOUND);
}
**//This is where the problem is, I want to save the token in ConfirmEmail column**
await this.userModel.updateOne({confirmEmail: token});
return link;
}
I will appreciate your suggestions or if there is a better way to do this
Thanks
updateOne needs 2 parameters, a filter to identify which document to modify, and a update indicating what to do.
.updateOnde({"_id":userId},{"$set":{"confirmEmail": token}})

ES6 Async/Await, ExpressJS and Postgres transactions

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))
}

Why can't I rename this object in my node application?

I've got a node.js/express application communicating with a Postgres database using the node-postgres module. It's working with async/await, but if I rename the object being returned to anything other than { rows }, it comes back undefined. Here is my code; note the comment right under the async, and above const { rows } = ...:
var express = require('express');
var router = express.Router();
const { Pool } = require('pg');
const pool = new Pool({
connectionString: 'postgresql://postgres#localhost/mydb'
});
router.post('/login', function(req, res, next) {
var username = req.body.username;
var password = req.body.password;
(async () => {
// if I rename this from "rows" to, say, "userdetails", it comes back undefined
const { rows } = await pool.query(`
SELECT id, password
FROM myschema.users
WHERE username = $1
LIMIT 1;`,[username]);
if (rows.length) {
// check password, etc, etc.
return res.status(200).send();
} else {
return res.status(401).send();
}
})().catch(e => setImmediate(() => {
res.status(500);
}
));
});
module.exports = router;
I'm sure I'm missing something pretty basic here, but why can't I rename this? Does the pg module dictate somehow that the returned var/const must be named { rows }? If so, how might I discover that? I set a breakpoint at the await and step through the code, but it's still unclear to me.
When you do const { rows } = xxx, you are using object destructing to assign the xxx.rows property to a new variable in the local scope that has the same name as the property.
Thus when you change the name of that property to something else:
const { myVar } = xxx;
The code is now looking for the property xxx.myVar which likely does not exist and thus ends up undefined.
If you want to use a different name, then you have to either use a different form of object destructuring assignment (that includes a new name) or don't use destructuring at all and just assign the .rows property to a new variable.
For example, you could do this instead:
const result = await pool.query(`
SELECT id, password
FROM myschema.users
WHERE username = $1
LIMIT 1;`,[username]);
const myVar = result.rows;
Or you could designate a new name in the object destructuring assignment:
const {rows: myVar} = await pool.query(`
SELECT id, password
FROM myschema.users
WHERE username = $1
LIMIT 1;`,[username]);
console.log(myVar);
This is called Object destructuring. When you do something like this
const { row } = someObject
then the variable row is equal to the row property of someObject. But if someObject does not even have any row property, then row obviously will be undefined.
Same is happening in your case. The object returned by await pool.query(... have rows property, thats why const { rows } works but it does not have userdetails property hence undefined.

Resources