You have questions about Firebase, and the Firebase community has answers!
But do you know the best place to get your questions answered?
It can be kind of overwhelming to figure out which destination is the best for your particular question. What I'd like to explore today are your options, and how to choose the best one. Choosing the best (and knowing how best to ask) could give you a huge advantage in getting the answers you need. There are five leading options, each with a different purpose. Let's explore those options!
It might seems silly to say, but I think your question should start with a Google search. A well-constructed search could surface existing answers in some of the forums I'll discuss next. You can't pass up the chance of getting your question answered immediately because someone else asked it first!
Try entering your question directly into the search box. Or, if you have an error message in your code, try copying it in there. For exact error messages, sometimes it's helpful to put your search string in quotes to force the most relevant matches to the top of the results.
Bear in mind that not every search yields good results. In some cases, you might stumble across something so rare that only one other person has ever seen it!
That time I searched for "ERROR: Walrus not found: have you checked the polar ice caps?"
If a search doesn't give what you're looking for, it's time to choose from some other options.
But before we continue - if you end up deciding that your question is appropriate for multiple forums, be sure to state that you've cross-posted your question. That gives everyone a chance to figure out which forum may be the best options, and if the question has already been answered elsewhere. That saves everyone time.
Stack Overflow is great for programming questions. In fact, that's the only kind of question you're supposed to ask there. Many Firebase team members pay attention there, including members of the greater community. There are a few tips to making the best of your question on SO:
Not all questions are good for Stack Overflow. In particular, your question may be closed by the community if it's any of the following:
If your question is closed by the community, it will almost certainly not be answered. But don't worry: for questions about Firebase that don't follow the Stack Overflow requirements, there are other options!
Quora is great for general questions, especially those seeking recommendations and opinions. There doesn't have to be a "correct" answer to a question on Quora. It's OK to ask broad questions.
If you choose Quora, be sure to tag your question with the Firebase topic so it's more likely to get noticed by people who have experience with Firebase.
firebase-talk is a long-standing mailing list for people who are looking for open-ended discussion about all things Firebase. It's great for open-ended discussions that require a lot of text that goes back and forth between group members. Many Firebase team members scan the messages here.
When you post your first message here, be prepared to wait for some time for a moderator to accept it.
The Firebase Slack is where Firebase enthusiasts gather to talk about, well, Firebase! This is good for general chit-chat and gathering opinions. While some Firebase team members check in from time to time, it's not an official support channel. So if you have a question that better fits Stack Overflow or Quora, I think it's better to ask there first.
One notable exception is the Firebase Test Lab team who use the #test-lab channel for direct support.
Here's a couple tips for using the Firebase Slack effectively:
This is a good place to ask for urgent issues such as problems with your production app. It's also good for troubleshooting if you require some back and forth with a real person with a problem that can be reproduced in code. You can expect a response from support within 24 hours of asking your question.
Firebase support also handles bug reports and feature requests. So if you have one of those, please fill out the form in that link.
If you want a timely response, I would avoid the following destinations for general questions:
The other reason I would avoid these is because they have limited visibility to the world, whereas the destinations above are well known by the Firebase community. You probably want your question to reach the maximum number of people as possible, in order to get answered quickly.
However, we do encourage you to use Twitter and other social media to broadcast the questions you ask on other sites. For example, it's good to ask a question on Stack Overflow or Quora, then tweet the URL of the question with the hashtag #AskFirebase. Your questions may get picked up for use on the Firebase channel on YouTube.
The Firebase community loves to help with Firebase! And it's easier to get help if you follow the guidelines here to make sure your questions reach the correct audience.
Now watch this video with Kato Richardson, who loves our Firebase community!
I'm always amazed when I think about the fact that Firebase Cloud Messaging sends billions of messages a day. Developers on iOS, Android, and web rely on FCM to send notifications and data messages to their clients. We recently updated FCM to incorporate better security and new features, and now you can access the new FCM through the Firebase Admin SDK for Node.js, Python, Java, and Go.
The latest version of FCM, called FCM v1, includes the ability to send a single request with platform-specific overrides. For example, you can set a time to live for an Android notification and an APNs priority for an iOS notification in the same body. See the documentation or this blog post for more information on platform overrides.
You can now conveniently integrate your server-side applications with the FCM v1 API using the Firebase Admin SDKs. Here's how you can send a simple data message in Node.js:
var registrationToken = 'YOUR_REGISTRATION_TOKEN'; var message = { data: { score: '850', time: '2:45' }, token: registrationToken }; admin.messaging().send(message) .then((response) => { console.log('Successfully sent message:', response); }) .catch((error) => { console.log('Error sending message:', error); });
To see this code in Java, Python, and Go, check the documentation. To see all message payload options, check out the FCM v1 documentation. In addition to sending messages to devices and topics, the Admin SDK for FCM also allows you to manage topic subscriptions from your server.
Here's an example of subscribing to a topic in Node.js:
var registrationTokens = [ 'YOUR_REGISTRATION_TOKEN_1', // ... 'YOUR_REGISTRATION_TOKEN_n' ]; admin.messaging().subscribeToTopic(registrationTokens, topic) .then(function(response) { console.log('Successfully subscribed to topic:', response); }) .catch(function(error) { console.log('Error subscribing to topic:', error); });
To see this code in Java, Python, and Go, check the documentation. Thanks to the Admin SDK, it's even easier than ever to send messages! Check out the full documentation here. And if you build something cool with FCM, be sure to tell us about it! Tweet @Firebase and @ThatJenPerson and let us know how it's going!
Securing your Firebase Realtime Database just got easier with our newest feature: query-based rules. Query-based rules allow you to limit access to a subset of data. Need to restrict a query to return a maximum of 10 records? Want to ensure users are only retrieving the first 20 records instead of the last 20? Want to let a user query for only their documents? Not a problem. Query-based rules has you covered. Query-based rules can even help you simplify your data structure. Read on to learn how!
query
Security rules come with a set of variables that help you protect your data. For instance, the auth variable tells you if a user is authenticated and who they are, and the now allows you to check against the current server time.
auth
now
Now, with the query variable, you can restrict read access based on properties of the query being issued.
messages: { ".read": "query.orderByKey && query.limitToFirst <= 100" }
In the example above a client can read the messages location only if they issue an orderByKey() query that limits the dataset to 100 or less. If the client asks for more than 100 messages the read will fail.
messages
orderByKey()
The query variable contains additional properties for every type of query combination: orderByKey, orderByChild, orderByValue, orderByPriority, startAt, endAt, equalTo, limitToFirst, and limitToLast. Using a combination of these properties you can restrict your read access to whatever criteria you need. Get the full reference and see more examples in our docs.
orderByKey
orderByChild
orderByValue
orderByPriority
startAt
endAt
equalTo
limitToFirst
limitToLast
Another benefit of query-based rules is that they make it easier to manage a shallow data structure.
In the past you might index your items location by a user's id.
items
{ "items": { "user_one": { "item_one": { "text": "Query-based, rules!" } } } }
This structure made it easy to query and restrict item reads on a per-user basis.
{ "rules": { "items": { "$uid": { ".read": "auth.uid == $uid" } } } }
This is great because your user's items are secured, but it requires you to index off of the user's id, potentially replicating data or complicating your client code.
With query-based rules, you can now get the same security without the nesting!
{ "rules": { "items": { ".read": "auth.uid != null && query.orderByChild == 'uid' && query.equalTo == auth.uid" } } }
The above rule will restrict any read on a per-user basis without indexing by the "uid" key, which means you can write a simple query to retrieve a user's items without sacrificing security.
"uid"
Now the data structure is reduced by one level:
```
{ "items": { "item_one": { "text": "Query-based, rules!", "uid": "user_one" } } }
``
db.ref("items").orderByChild("uid") .equalTo(auth.currentUser.uid) .on("value", cb)
The above query will retrieve all the items belonging to the currently authenticated user. If a user tries to access items that don't belong to them, the read will fail.
Query expressions are new feature that can be used in parallel with your existing rules. If you find that query expressions can improve your security or simplify your structures you can upgrade to them incrementally.
Query-based rules are available for use today. Go to your Firebase console and use the emulator to give them a whirl. Check out our official documentation for more information. If you run into any problems make sure to check out our support channels or hit up the Firebase Slack community.
When I think back 10 years or so when I was writing code for a monolithic codebase written in C++, one of the worst memories I have of the time was waiting for a build to complete just to run a couple lines of new code. The pain was so common among engineers that it bore the creation of one of the all-time most popular XKCD comics:
My office at the time didn't have swords to play with, so waiting for a build to complete was really boring.
The pain is similar if I had to deploy my Cloud Functions code every time I want test a function. Fortunately, the Firebase CLI now has a local emulator that works for both HTTPS and other types of functions. This lets me run the functions on my development machine by emulating the events that would trigger it. You can read more about that in the documentation.
One of the emulator's handy features is the fact that it will reload any JavaScript files that change over time. So, if you modify the code of a function, you can test it right away in the emulator without having to restart it:
It's fast and easy, and keeps you "in the zone".
However, things become less easy when you switch from JavaScript to TypeScript. Running TypeScript on Cloud Functions requires a compilation phase to convert the TypeScript into JavaScript so that it can be executed by the emulator. It adds a bit of a hassle:
npm run build
There's an easier way to do this that doesn't involve running a command every time. The TypeScript compiler (tsc) has a special "watch mode" that lets it pick up changes to your TypeScript and automatically compile them. You can run it from the command line by changing to your functions folder and running this command:
tsc
./node_modules/.bin/tsc --watch
With this running alongside the local emulator, which automatically picks up changes to the compiled JavaScript, your development workflow goes back to this:
And, if you're using VSCode, you can even have it directly run tsc in watch mode. You can start it up with Tasks -> Run Build Task..., then choosing "tsc: watch".
It will fire up a command shell right in the IDE, run tsc --watch, and show you the output. You'll see messages like this, along with any compilation errors, any time a TypeScript file changes:
tsc --watch
2:38:38 PM - File change detected. Starting incremental compilation... 2:38:38 PM - Compilation complete. Watching for file changes.
Now, bear in mind that tsc in watch mode won't run TSLint against your code, so be sure to set that up in VSCode so you can always see its recommendations, as I showed in a prior blog post. If you're not linting your code, seriously consider setting that up, because you'll catch potential mistakes that could be costly in production.
Are you looking for something new to learn this year? Then let me suggest TypeScript for development with Cloud Functions!
Not long ago, the Cloud Functions for Firebase team released an update to the Firebase CLI that makes it easy for you to write your functions in TypeScript, rather than JavaScript. The Firebase team encourages you to consider switching to TypeScript, but I can imagine you might be reluctant to learn a new language, especially if you're already comfortable with JavaScript. The great news is that TypeScript offers you a bunch of benefits that are easy to start using today.
The language itself offers a bunch of features that make your code easier to read and write, and less error-prone:
There's also async/await from ECMAScript 2017, which helps you write asynchronous code more easily. The primary challenge with asynchronous code is management of promises, which is crucial to get right when writing Cloud Functions, but difficult to master. TypeScript makes that much easier.
But what I really want to dive into here is an excellent tool called TSLint that can check your TypeScript code for potential problems before you deploy to Cloud Functions. The Firebase CLI will prompt you to configure TSLint when you initialize Functions in a project using TypeScript, and we strongly recommend that you opt into it.
When you opt into TypeScript and TSLint in your Firebase project structure, the Firebase CLI will add and modify some project files when you run firebase init. First let's take a look at functions/package.json. In there, you'll see the following key:
firebase init
functions/package.json
"devDependencies": { "tslint": "^5.8.0", "typescript": "^2.6.2" },
This is where node pulls in TypeScript and TSLint for development. Notice that there are "devDependencies" that are separate from the normal "dependencies" that you use in your function code. devDependencies are only stored on your machine, and are made available as tools for development. They are not deployed with your code. Also in that file, notice there are two script definitions:
"scripts": { "lint": "./node_modules/.bin/tslint -p tslint.json", "build": "./node_modules/.bin/tsc", ... }
These give you the ability to run npm run lint and npm run build on the command line from your functions directory. The first check your code with TSLint, and the second will build it with the TypeScript compiler.
npm run lint
The next file to look at is firebase.json. This now has a predeploy hook that runs TSLint against your code, so if it has an error, the deploy will fail:
firebase.json
{ "functions": { "predeploy": [ "npm --prefix $RESOURCE_DIR run lint", "npm --prefix $RESOURCE_DIR run build" ] } }
The next file is functions/tsconfig.json. This contains the configuration for the TypeScript compiler.
functions/tsconfig.json
{ "compilerOptions": { "lib": ["es6"], "module": "commonjs", "noImplicitReturns": true, "outDir": "lib", "sourceMap": true, "target": "es6" }, "compileOnSave": true, "include": [ "src" ] }
I won't cover all the settings here, but it's important to note that the compiler will look for source TypeScript files under functions/src, and compile them to JavaScript into functions/lib, with ECMAScript 6 compatibility, as required by Cloud Functions. Cloud Functions currently runs Node 6, which means it natively understand ES6 code.
functions/src
functions/lib
Lastly, take a brief look at functions/tslint.json. This lists all the rules that are observed when TSLint checks your code. You can add new rules here, or remove the rules you don't want. I suggest leaving everything there, as the list of rules was curated by the Firebase team to help with writing functions. The "strict errors" whose values are set to true will cause compile time errors if violated. The warnings below that will just complain about possible issues, and it's the team's opinion that you should look into resolving them.
functions/tslint.json
true
Alrightey. Take a look at this function that populates a property called createdAt when a user node is created in Realtime Database. Do you see what's wrong here?
createdAt
export const onUserCreate = functions.database.ref('/users/{uid}').onCreate(event => { event.data.ref.update({ createdAt: Date.now() }) })
TSLint sees the issue, and its one of the most common mistakes made when writing functions. If you run npm run build on this code, you'll see this error in its output:
Promises must be handled appropriately
This error is triggered by the rule no-floating-promises. TSLint sees that event.data.ref.update returns a promise, but nothing is being done with it. The correct way to deal with promises for database triggers is to return it:
event.data.ref.update
export const onUserCreate = functions.database.ref('/users/{uid}').onCreate(event => { return event.data.ref.update({ createdAt: Date.now() }) })
If you're using async/await, you can also declare the function async and use await to return it:
async
await
export const onUserCreate = functions.database.ref('/users/{uid}').onCreate(async event => { await event.data.ref.update({ createdAt: Date.now() }) })
Proper handling of promises is an absolute requirement when dealing with Cloud Functions, and TSLint will point out where you're not doing so.
I do a lot of Android development, and I'm accustomed to Android Studio linting my code as I type it. This is valuable because I get instant feedback about where things could go wrong. On the Firebase team, a bunch of us use VSCode for editing TypeScript, and it will use TSLint to give you instant feedback. The TSLint extension is easy to install.
Go to View -> Extensions. Type "TSLint" into the search box on the left. Find the TSLint extension, and click the Install button. After it's installed, click the Reload button, and now your TypeScript will be marked with possible mistakes.
Here's an HTTPS function that fetches a user document from Firestore using the Admin SDK. It looks OK and works OK:
export const getUser = functions.https.onRequest((req, res) => { var uid = req.params.uid const doc = admin.firestore().doc(`users/${uid}`) doc.get().then(doc => { res.send(doc.data()) }).catch(error => { res.status(500).send(error) }) })
But when viewed in VSCode with TSLint markers, you'll see that it violates some best practices:
Notice the squiggly lines under var, uid, and doc. If you hover the mouse over the marked code, you'll see a message from TSLint:
var
uid
doc
That's TSLint using the prefer-const rule to tell you that it's better to use const instead of var to declare values that don't change. It's good for the readability of your code, and also prevents someone from making an accidental change to it later on.
const
The squiggly line under doc is TSLint using the no-shadowed-variable rule to point out that the doc parameter in the function passed to then() (a DeltaDocumentSnapshot object) is masking the doc constant in the outer scope (a DocumentReference object), making the latter completely unavailable for use. While this is not really a bug here, it can lead to confusion about which doc instance is being referred to at any given moment. Renaming either instance resolves the issue.
then()
Here's a lint-free version of the same function:
export const getUser = functions.https.onRequest((req, res) => { const uid = req.params.uid const doc = admin.firestore().doc(`users/${uid}`) doc.get().then(snapshot => { res.send(snapshot.data()) }).catch(error => { res.status(500).send(error) }) })
This makes TSLint happy, which should make you happy, because you're writing better code!
If you haven't started learning TypeScript yet, 2018 is a fine time to begin. It's easy to get started, because TypeScript is a strict superset of JavaScript, meaning that all your existing JavaScript code is already valid TypeScript. So you can simply rename your .js files to .ts and drop them into functions/src. Then, you can start using TypeScript language features as you wish. Let us know how it goes and shout out to @Firebase on Twitter.