Estoy creando una API que verifica el valor del contador, incrementa en uno y luego crea nuevos documentos en la colección de documentos. Para ese propósito, estoy usando runTransaction() .
Y me estoy encontrando con un problema. Las transacciones funcionan como se esperaba, verifican el valor del contador y luego lo incrementan en uno. Pero después de eso, cuando intento configurar un nuevo documento, aparece un error (objeto vacío) y no puedo hacerlo. Creo que mi lógica utilizada puede ser mala, por lo que necesito su consejo donde me equivoco.
const db = admin.firestore() const couterRef = db.collection('invoices').doc('invoices_doc') let counterValue = 0 // check last invoice counter number and increment by 1 return db.runTransaction((transaction) => { return transaction .get(couterRef) .then((counter) => { counterValue = counter.data().counter + 1 // Update counter return transaction.update(couterRef, { counter: counterValue }) }) .catch((error) => response.status(200).send({ status: 'TRANSACTION ERROR', error })) }) .then(() => { // Create new invoice document couterRef.collection('invoices').doc(counterValue).set({ series: 'SS', series_nr: counterValue }) .then(() => response.status(200).send({ status: 'OK' })) .catch((error) => response.status(200).send({ status: 'DOCUMENT SET ERROR', error })) }) .catch((error) => { response.status(200).send({ status: 'RUN TRANSACTION ERROR', error }) })No he probado su código, pero lo más probable es que el problema provenga del hecho de que modifica el estado de la aplicación dentro de sus funciones de transacción , algo que debe evitar. Hay una sección específica sobre este problema en la documentación.
Debe pasar el nuevo valor de counterValue fuera de su función de transacción , de la siguiente manera:
const db = admin.firestore(); const couterRef = db.collection('invoices').doc('invoices_doc'); // let counterValue = 0 Remove this line return db .runTransaction((transaction) => { return transaction.get(couterRef).then((counter) => { const counterValue = counter.data().counter + 1; // See the changes below !! transaction.update(couterRef, { counter: counterValue }); // Actually, this in not an asynchronous operation return counterValue; // We pass the new value of counterValue out of the transaction function }); }) .then((counterValue) => { // Create new invoice document couterRef.collection('invoices').doc(counterValue.toString(10)).set({ series: 'SS', // you missed a , series_nr: counterValue, }); }) .then(() => response.status(200).send({ status: 'OK' })) .catch((error) => { response.status(200).send({ status: 'RUN TRANSACTION ERROR', error }); });Además, en su función de nube HTTPS, no envíe la respuesta al cliente desde DENTRO de la transacción: esto también es una modificación del estado de la aplicación y no se debe realizar desde dentro de la transacción.
Del mismo modo, no incluya bloques catch en los bloques then : agregue un bloque catch solo una vez al final de la cadena de promesa . Si desea tratar con diferentes tipos de errores en este bloque catch único, simplemente arroje errores con diferentes mensajes de error y decida, en el bloque catch , qué hacer según el mensaje. Alternativamente, puede crear algunas subclases de la clase Error.
Habiendo dicho todo eso, dado que su transacción solo afecta un documento y solo incrementa un contador, podría muy bien usar el método FieldValue.increment() , que es atómico. Consulte esta publicación de blog de Firebase para obtener más detalles.