Cloud Functions - Cloud Firestore error: can't get serverTimestamp
const admin = require('firebase-admin');
exports.userlog = functions.firestore
.document('user/{userId}')
.onUpdate((change, context) =>
{
const db = admin.firestore();
//var timestamp = db.FieldValue.serverTimestamp();
var timestamp = db.ServerValue.TIMESTAMP;
...
return db.collection('userlog').add(
{
userId : previousValue.userId,
...
timestamp: timestamp
}).then(ref =>
{
return console.log('Added document with ID: ', ref.id);
});
});
I got two errors separately:
TypeError: Cannot read property 'serverTimestamp' of undefined
TypeError: Cannot read property 'TIMESTAMP' of undefined
The correct syntax is:
firebase.firestore.FieldValue.serverTimestamp()
Note the lack of parenthesis (()) after firestore: this is a static variable, not an instance variable/member field.
// Imports: Dependencies
const admin = require('firebase-admin');
// Correct Syntax (If You Don't Want To Import Firestore)
created_at: admin.firestore.FieldValue.serverTimestamp(),
since #FrankvanPuffelen's edit queue is full with missing imports, documentation, etc I'll post an update on his solution
include from (web version 8):
const firebase = require('firebase-admin');
or use more recent (web version 9):
import {FieldValue} from "firebase/firestore";
The correct syntax is:
firebase.firestore.FieldValue.serverTimestamp()
Note the lack of parenthesis (()) after firestore: this is a static variable, not an instance variable/member field.
See Firebase example for more info
I was using the correct syntax as per the upvoted answer(s), but still go this error. It turns out it had something to do with my version of the firebase CLI being off-sync with versions of the firebase-admin package (I'm not sure how it happened in the first place, but I noticed form my source tracking tool that the package.json and lock files had been updated).
Solved the problem by deleting all changes I had made on my package.json & lock files and re-installed the CLI:
npm i -g firebase-tools
Related
I'm trying to create a new document when a user signs up for my app.
However, 'exports' is returning "Uncaught (in promise) ReferenceError: exports is not defined".
The code below is handling the function. I do also have an onAuthStateChanged function that switches some logged-in/out elements, although I don't think that could be stopping exports from being defined.
import { createUserWithEmailAndPassword, onAuthStateChanged,
signInWithEmailAndPassword } from "firebase/auth";
import { db, auth } from "./firebase";
import { collection, doc, setDoc, addDoc } from "firebase/firestore";
const signUpForm = document.querySelector('#signup-form');
if (signUpForm) {
signUpForm.addEventListener('submit', (e) => {
e.preventDefault();
//get user info
const email = signUpForm['signup-email'].value;
const password = signUpForm['signup-password'].value;
createUserWithEmailAndPassword(auth, email, password).then((cred) => {
const overlay = document.getElementById('overlay');
overlay.classList.add('hidden');
overlayP.classList.remove('hidden');
signUpForm.reset();
exports.createUserDoc = functions.auth.user().onCreate((user) => {
return admin.firestore().collection("users").doc(user.uid).setDoc({
email: user.email,
uid: user.uid,
})
});
// document.getElementById("signUpErr").innerHTML = "";
})
// .catch(err => {
// document.getElementById("signUpErr").innerHTML = err.message;
// });
});
};
I have initialized firebase and installed express.js within my index.js file but am I missing something to make sure this parameter is defined? I'm using Vite as a package bundler and node.js.
I'm new to coding and firebase, any advice would be massively appreciated.
I managed to solve this issue by taking a few days to read the documentation and understand what is going on with Cloud functions. The benefit of having a cloud function is that you can create triggers to your database that is away from your client-side code, improving security.
I was trying to call the cloud function within my app.js file and not within the firestore functions index.js file created when initializing firebase. Here you import through CommonJS Modules (CJS) the required SDK, in my case it was functions and admin.
Now my cloud functions live within index.js in /Functions folder separate from my app files.
// The Cloud Functions for Firebase SDK to create Cloud Functions and set up triggers.
const functions = require('firebase-functions');
// The Firebase Admin SDK to access Firestore.
const admin = require('firebase-admin');
admin.initializeApp();
I also had to change the .setDoc() function to .set() as this was a Type error. I also added a userDelete function to delete users' documents in firestore.
exports.createUserDoc = functions.auth.user().onCreate(user => {
// Your new auth record will have the uid and email on it because
// you used email as the way to create the auth record
return admin.firestore().collection("users").doc(user.uid).set({
email: user.email,
bookmarked: []
})
});
exports.userDeleted = functions.auth.user().onDelete(user => {
const doc = admin.firestore().collection("users").doc(user.uid);
return doc.delete();
});
I would also like to note that setting up the firestore emulator has been very useful in this process and I'm sure will help me develop and test the other functions I need to create my app.
I used npm to install the Binance API node. I reach to the following step:
import Binance from 'binance-api-node'
const client = Binance()
// Authenticated client, can make signed calls
const client2 = Binance({
apiKey: 'xxx',
apiSecret: 'xxx',
getTime: xxx, // time generator function, optional, defaults to () => Date.now()
})
client.time().then(time => console.log(time))
Could you please guide me on how to run this part. The node module could be found on https://github.com/Ashlar/binance-api-node
You'll need to create your API key following this doc
2. Once you have your API key and secret key, you store in apiKey and apiSecret respectively.
3. If you do not have an appropriate babel config, you will need to use the basic commonjs requires.
const Binance = require('binance-api-node').default instead of ES6 import
I want to make a cloud function that uses 'firebase' module (not a 'firebase-functions')
And when I'm using or even only import it, npm throws an error:
Error: Error parsing triggers: Failed to load gRPC binary module because it was not installed for the current system
Expected directory: node-v64-darwin-x64-unknown
Found: [node-v79-darwin-x64-unknown]
This problem can often be fixed by running "npm rebuild" on the current system
Original error: Cannot find module '/Users/rame/functions/node_modules/grpc/src/node/extension_binary/node-v64-darwin-x64-unknown/grpc_node.node'
1) If you want to compile the package/file into executable, please pay attention to compilation warnings and specify a literal in 'require' call. 2) If you don't want to compile the package/file into executable and want to 'require' it from filesystem (likely plugin), specify an absolute path in 'require' call using process.cwd() or process.execPath
here's my code on Type script:
import * as functions from 'firebase-functions';
import admin = require('firebase-admin');
//the cause of an error
import * as firebase from 'firebase';
admin.initializeApp()
export const getProfilePicture = functions.https.onRequest((request, response) => {
//also there
const uid = firebase.auth().currentUser?.getIdToken
const promise = admin.storage().bucket().file('usersPfp/' + uid).getSignedUrl({
action: 'read',
expires: '03-09.2441'
})
const p2 = promise.then(GetSignedUrlResponse => {
const data = GetSignedUrlResponse[0]
return response.send({"data": data})
})
p2.catch(error =>{
console.log(error)
return response.status(500).send({"error": error})
})
})
How to fix that?
What you're doing isn't supported. The Firebase Authentication JavaScript client library isn't supported for use in backend environments like Cloud Functions.
The idea of a current user:
firebase.auth().currentUser
only makes sense in the client app where the user is signed in. It's not something that's known on the backend.
What you can do instead is send the user's ID token from your client to your function, the use the Admin SDK to verify it, then perform some actions on the user's behalf.
I am trying to fetch a value from my collection using the createdAt property, which is a timestamp.
This is roughly what my query looks like:
function getDataFromYesterdayToNow (db){
const now = new Date()
const yesterday = new Date(now.setDate(now.getDate() - 1))
yesterday.setHours(0, 0, 0, 0)
const { Timestamp } = firebase.firestore
return db
.collection('myData')
.where('createdAt', '>=', Timestamp.fromDate(yesterday))
.where('createdAt', '<=', Timestamp.fromDate(now))
.get()
}
However, when I run this, I get the following error:
error was 'FirebaseError: [code=invalid-argument]: Function Query.where() called with invalid data. Unsupported field value: a custom Timestamp object'. Stacktrace was 'FirebaseError: Function Query.where() called with invalid data. Unsupported field value: a custom Timestamp object
I am super confused, I have always fetched using Timestamp objects in other collections, and it does not work if I try to just use the date object. Have I forgotten something?
Edit: as requested, here is an example of what my doc looks like:
{
name: "My Data Name", // (string)
createdAt: November 9, 2018 at 8:40:45 PM // (Timestamp)
}
I had the same issue. There are two things you need to check.
1. Am I using the right SDK?
Did you know there are two different firebase SDK you can use? One is a client SDK (firebase-js-sdk a.k.a. firebase package) and the other one is a firebase server SDK (nodejs-firestore .a.k.a. #google-cloud/firebase package). These two libraries have its own implementation on firestore.Timestamp class and THEY ARE NOT COMPATIBLE.
Some of the other NPM packages dependencies as follow:
"#firebase/firestore" (*)
-> "firebase" (client SDK which imports all #firebase/* except #firebase/testing)
-> "#angular/fire" (and other client libraries with firebase binding)
-> "#firebase/testing" (mocking Firestore client)
"#google-cloud/firebase" (*) (server SDK)
-> "firebase-admin"
-> "firebase-functions-test"
Where (*) denotes the location of firestore.Timestamp definition.
In short, you should use corresponding Timestamp.
Case 1. Use a client SDK only
import { firestore, initializeApp } from 'firebase';
import { config } from './my-firebase-config';
const app = initializeApp(config);
app.firestore().collection('users')
.where('createdAt', '<=', firestore.Timestamp.fromDate(new Date()))
.get();
Case 2. Use a server SDK only
import { firestore, initializeApp } from 'firebase-admin';
const app = initializeApp();
app.firestore().collection('users')
.where('createdAt', '<=', firestore.Timestamp.fromDate(new Date()))
.get();
Case 3. Mixing SDKs
Sometimes you need to use client SDK (specifically, #firebase/testing) when you're testing code that are run on server (e.g. firebase functions.)
// server.ts
import { firestore, initializeApp } from 'firebase-admin';
const app = initializeApp();
app.firestore().collection('users')
.where('createdAt', '<=', fs.Timestamp.fromDate(new Date()))
.get();
// server.test.ts
import { firestore } from 'firebase';
import { initializeAdminApp } from '#firebase/testing';
// Replace server sdk with client sdk
jest.mock('firebase-admin', () => ({
firestore,
initializeApp: () => initializeAdminApp()
}));
2. Am I using the right version?
If you're using the correct SDK, the next thing to check is whether you're using the same version of Timestamp implementation. If you're using Client SDK for example, then you should check your package-lock.json whether it has a unique version of firebase.
For my case, I installed #firebase/testing and firebase in a different time, and due to the difference firebase version dependency from #firebase/testing, I had two different firebase packages installed at the same time. You can update an old package to fix this.
Actually, the title is more or less the whole explanation of the problem.
I am trying to use Firebase inside my React app, which also uses NextJS and the problem is that I cannot get storage to work.
import firebase from 'firebase'
import uuid from 'uuid/v4'
// Init
try {
firebase.initializeApp({
apiKey: 'apiKey',
authDomain: 'authDomain',
databaseURL: 'dbUrl',
projectId: 'projID',
storageBucket: 'storageBucket',
messagingSenderId: 'id'
})
} catch (err) {
if (!/already exists/.test(err.message)) {
console.error('Firebase initialization error', err.stack)
}
}
console.log(firebase.app().name) // <- name
// References
const database = firebase.database()
const storage = firebase.storage().ref() // <- the problem
const documentImageStorage = storage.child('images/')
const documentsRef = database.ref('/documents/')
const documentsRequestsRef = database.ref('/requests/')
So, as I run the code I can confirm that the app works as the database works properly and the name ([default]) is returned correctly, but the line in which the storage reference is defined returns an error:
Firebase: firebase.storage() takes either no argument or a Firebase App instance. (app/invalid-app-argument).
Any ideas why this might happen? How can I solve it?
(Firebase Storage JS dev)
I was able to reproduce your error in Next.js. I'm not super familiar with it, but I understand Next.js does React-y server-side rendering, so the code you write for your page will generally be executed in the node server.
Unfortunately, Storage isn't supported in node right now, which includes server-side rendering contexts (feel free to leave a comment in the Github issue about your use case).
It should work in normal React apps (i.e. client-side code) though.
EDIT: found a (barely) workaround
The code appears to not crash in Next.js if you add an import at the top of the file:
import firebase from 'firebase'
import _s from 'firebase/storage'
import uuid from 'uuid/v4'
...
Regardless, the Storage library still isn't supported in node. Most anything interesting (uploading objects, getting object metadata) won't work, so unless all you wanted to do was call storage.toString() somewhere this probably doesn't solve your problem.
Firebase docs states that you save storage service reference to variable, then save storage reference to different variable.
https://firebase.google.com/docs/storage/web/create-reference
So I would do this like so:
const storage = firebase.storage();
const storageRef = storage.ref();
You can also put path to your storage folder to get reference for it, like this:
const documentImageStorage = storage.ref('/images/');
Ok, so I figured out how to make it work!
export const storage = process.browser ? firebase.storage().ref() : undefined
This way, storage part, which is unavailable on the backend isn't loaded, but on the frontend it is and everything works perfectly!
Thanks for both answers!