Firebase User Authentication - javascript

Below is my working GET API, however it is using admin.firestore.
As I am a new programmer, I am having difficulties incorporating the Authentication portion
app.get("/", async (req, res) => {
const snapshot = await admin.firestore().collection("users").get();
let user = [{records:[]}];
snapshot.forEach((doc) => {
let id = doc.id;
let data = doc.data();
users.records.push({ id, ...data });
});
res.status(200).send(JSON.stringify(users));
});
What I hope to achieve is that users can only access their own database hopefully something like this
app.get("/", async (req, res) => {
const snapshot = await [user uid's reference].collection([grab user's uid and insert here]).get();
let [insert user's uid here] = [{records:[]}];
snapshot.forEach((doc) => {
let id = doc.id;
let data = doc.data();
[insert user's uid here].records.push({ id, ...data });
});
res.status(200).send(JSON.stringify([insert user's uid here]));
});

The user is not an object but an array, so you cannot use user.records:
// This is invalid
let users = [{records:[]}];
console.log(users.records)
// This is valid
let users = {records: []}
console.log(users.records) // []
You can directly use map() on the QuerySnapshot:
app.get("/", async (req, res) => {
// Get a QuerySnapshot of users collection
const snapshot = await admin.firestore().collection("users").get();
// Map an array containing user data
const users = snapshot.docs.map(d => ({id: d.id, ...d.data()}))
// return the response
return res.status(200).send(JSON.stringify(users));
});

Related

Why Firebase query with orderByChild returns a link?

According to the docs, I use this code to get the players in ascending order:
const db = getDatabase();
const playersByPoints = query(
ref(db, 'usersPoints'),
orderByChild('points'),
);
But when I console.log(playersByPoints) I get this:
"https://app-name.firebaseio.com/usersPoints"
Here is the database:
Am I missing something?
Thanks!
Your playersByPoints here is a Query object, which when logged returns the string of the linked database reference.
You need to actually invoke the query using either get(q) or onValue(q) to get the data you are looking for.
// one-off
const playersByPointsQuery = query(
ref(db, 'usersPoints'),
orderByChild('points'),
);
const playersByPointsQuerySnapshot = await get(playersByPointsQuery);
const playersByPoints = [];
playersByPointsQuerySnapshot.forEach(childSnapshot => {
playersByPoints.push({
...childSnapshot.val(),
_key: childSnapshot.key
});
});
// todo: do something with playersByPoints
or
// realtime listener
const playersByPointsQuery = query(
ref(db, 'usersPoints'),
orderByChild('points'),
);
const unsubscribe = onValue(
playersByPointsQuery,
{
next: (playersByPointsQuerySnapshot) => {
// new data available
const playersByPoints = [];
playersByPointsQuerySnapshot.forEach(childSnapshot => {
playersByPoints.push({
...childSnapshot.val(),
_key: childSnapshot.key
});
});
// todo: do something with playersByPoints
},
error: (err) => {
// error
// todo: handle
}
}
);

mongoose how to send transaction multiple collections

Side-Note I connect to DB with the following code:
const mongoose = require('mongoose');
const connectDB = (url) => {
return mongoose.connect(url);
}
Problem Description:
I have two different Collections. Both Operations, findByIdAndUpdate and create must run as an atomic operation. This should be possible with mongoose Transactions.
const registerCustomer = async (req, res) => {
await CustomerRegistrationCode.findByIdAndUpdate(req.body._id, { used: true });
const customer = await Customer.create({firstName: req.body.firstName});
}
What I tried:
const registerCustomer = async (req, res) => {
const session = await mongoose.startSession();
await session.startTransaction();
try {
await CustomerRegistrationCode.findByIdAndUpdate(req.body._id, { used: true }); //updates even though
const customer = await Customer.create({ firstName: req.body.firstName });// this line will throw error
await session.commitTransaction();
session.endSession();
} catch (error) {
console.error('abort transaction');
await session.abortTransaction();
session.endSession();
throw error;
}
}
Problem The CustomerRegistrationCode Collection gets updated even though the Customer.create method throws an error. How can this be solved?
New approach to understand MongoDB Transactions fails, but this is official code from https://mongoosejs.com/docs/transactions.html
const mongoose = require('mongoose');
const debugMongo = async () => {
const db = await mongoose.createConnection("mongodb://localhost:27017/mongotest");
const Customer = db.model('Customer', new mongoose.Schema({ name: String }));
const session = await db.startSession();
session.startTransaction();
await Customer.create([{ name: 'Test' }], { session: session }); //(node:20416) UnhandledPromiseRejectionWarning: MongoServerError: Transaction numbers are only allowed on a replica set member or mongos
let doc = await Customer.findOne({ name: 'Test' });
assert.ok(!doc);
doc = await Customer.findOne({ name: 'Test' }).session(session);
assert.ok(doc);
await session.commitTransaction();
doc = await Customer.findOne({ name: 'Test' });
assert.ok(doc);
session.endSession();
}
debugMongo();
At Customer.create an error gets thrown and i don't know why. Does somebody have an minimal working example?
You are using the transaction in a wrong way, that is why it does not work.
You need to pass the session object to your operations.
const registerCustomer = async (req, res) => {
const session = await mongoose.startSession();
session.startTransaction();
try {
await CustomerRegistrationCode.findByIdAndUpdate(req.body._id, { used: true }, { session });
const customer = await Customer.create({ firstName: req.body.firstName }, { session });
await session.commitTransaction();
} catch (error) {
console.error('abort transaction');
await session.abortTransaction();
} finally {
session.endSession();
}
}
Also, I have refactored your code a bit.
You can read more about transactions here

How do I return each collection and its documents in firestore with cloud functions?

Would like to return an array that has [1year, 1month, etc] and each of those are arrays that contain each document.
Currently, this returns an empty array but when I print the size of the snapshots I get the correct values. Not sure if i'm using push() correctly or if this is an async issue. Thanks.
exports.getStockPrices = functions.https.onRequest((req, res) => {
cors(req, res, () => {
const currentUser = {
token: req.headers.authorization.split('Bearer ')[1]
};
// ! this is a post request
admin
.auth()
.verifyIdToken(currentUser.token)
.then(decodedToken => {
// keep this just in case we want to add anything to do with the user
const user = decodedToken;
// array of collections e.g [1year, 1mo, etc]
const data = [];
// array of documents e.g [18948901, 1984010471, etc]
const documents = [];
db.collection('historical')
.doc(`${req.body.ticker}`)
.listCollections()
.then(collections => {
// each collection is the 1year, 1mo, etc
collections.forEach(collection => {
collection.get().then(querySnapshot => {
console.log('number of documents: ' + querySnapshot.size);
querySnapshot.forEach(doc => {
// doc.data is each piece of stock data
documents.push(doc.data());
});
// each document e.g 1year, 1mo, etc
data.push(documents);
});
});
return data;
})
.then(data => {
return res.json({ data });
})
.catch(err => {
console.log(err);
return res.status(500).send({ error: 'error in getting data' });
});
})
.catch(err => {
console.log(err);
return res.status(500).send({
error: 'error authenticating user, please try logging in again'
});
});
});
});
Due the nature of async calls, your return occurs before your array is being filled.
You can try my example, my firebase function is defined as async this allows me to use await, this statement allows to add a kind of sync for your firestore operations by waiting for the promises.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
exports.eaxmple = functions.https.onRequest(async (req, res) => {
var datax = []
var collections = await db.collection('collection').doc('docid').listCollections()
for (collection in collections) {
content = await collections[collection].get().then(querySnapshot => {
console.log('number of documents: ' + querySnapshot.size);
return querySnapshot.docs.map(doc => doc.data());
});
datax.push(content)
}
return res.json({datax});
});

How can I get the data of another document in the firebase function?

How do I get firestore data in firebase functions?
var number;
await db.collection("users").doc(uid).collection("info").doc("info").get().then(doc => {
number = doc.data().get("countUnMutable").toString;
return db;
});
I want to write the document to a different address using the received value, but it is not written.
exports.changeLocation = functions.region('europe-west1').firestore.document('users/{userId}/firstNotes/{noteId}').onCreate(async (snap,context) => {
const uid = context.params.userId;
const data = snap.data();
const noteId = context.params.noteId;
var number;
await db.collection("users").doc(uid).collection("info").doc("info").get().then(doc => {
number = doc.data().get("countUnMutable").toString;
return db;
});
await db.collection('users').doc(uid).collection("notes").doc("firebaseNote:".concat(number)).set(data);
const alreadyExistedDocument = db.collection('users').doc(uid).collection('notes').doc(noteId);
await alreadyExistedDocument.delete();
});

how to get the unique id in firebase

ive had a lot of trouble with firebase arrays, im now using push
I have this
I want to pull all the users down so I do this:
export const pullFromFirebase = () => {
return firebase
.database()
.ref("/users/")
.once("value")
.then(snapshot => {
var users = [];
snapshot.forEach(user => {
users.push(user.val());
});
return users;
});
};
this is fine
however, I now need the unique id -LI7d_i_BmrXktzMoe4p that firebase generated for me so that I can access this record (i.e. for updating and deleting) how do i do this?
You can get the key of the snapshot with the key property: https://firebase.google.com/docs/reference/js/firebase.database.DataSnapshot#key
So you could change your code to this:
export const pullFromFirebase = () => {
return firebase
.database()
.ref("/users/")
.once("value")
.then(snapshot => {
var users = [];
snapshot.forEach(user => {
let userObj = user.val();
userObj.id= user.key;
users.push(userObj);
});
return users;
});
};
You can change this part:
snapshot.forEach(user => {
users.push(user.val());
});
To instead be:
let usersObj = snapshot.val()
for (var user_id in usersObj) {
users.push(Object.assign(usersObj[user_id], {
user_id: user_id,
});
});
That way, instead of each element in the users array only having email, name and username fields, it has user_id as well.

Categories