The Cloud Functions for Firebase team is pleased to provide a turnkey solution for secure, scheduled functions using Firebase tools and SDKs.
One of the most common questions about Google Cloud Functions is how to schedule a function for regular execution, similar to a cron job. In the past, the Firebase team recommended solutions involving App Engine or third party schedulers as a scheduling mechanism to invoke a function, which works fine. However, solutions that involve the invocation of an HTTP function aren’t fully secure, because the HTTP endpoint URL is effectively public and accessible to anyone who knows that URL. And, solutions that involve creating and sending messages to a Cloud Pub/Sub endpoint can be difficult to manage.
Recently, Google Cloud released Cloud Scheduler, which allows you to schedule HTTP requests or Cloud Pub/Sub messages to functions that you deploy. You can follow a tutorial for getting that set up in your project, if you like. But today, it gets even easier than that, and it doesn’t require working with the Google Cloud console.
Firebase now supports a new type of Pub/Sub function, built on top of Cloud Scheduler, that automatically configures Cloud Scheduler, along with a Pub/Sub topic, that invokes a function that you define using the Cloud Functions for Firebase SDK.
If you’re using version 2.3.0 of the firebase-functions NodeJS module, along with 6.7.0 of the Firebase CLI, you can now define functions that use Google Scheduler’s standard cron syntax like this:
export scheduledFunctionCrontab = functions.pubsub.schedule('5 11 * * *').onRun((context) => { console.log('This will be run every day at 11:05 AM UTC!'); });
You can schedule invocations using an English description as well:
export scheduledFunctionPlainEnglish = functions.pubsub.schedule('every 5 minutes').onRun((context) => { console.log('This will be run every 5 minutes!'); });
To get started with scheduled functions using Firebase tools, navigate to the documentation, and check out this sample available in GitHub. Your project must be on the Blaze payment plan, as Cloud Scheduler and Pub/Sub require billing information. Each Cloud Scheduler job costs $0.10 (USD) per month, and there is an allowance of three free jobs per Google account. Besides the normal costs for Cloud Functions, the costs for this feature are the same as if you set up the underlying infrastructure manually - for more information, check out the full pricing information for Cloud Scheduler and Cloud PubSub.
At Firebase, our mission is to help mobile app teams succeed, which means having the capabilities to support companies and teams of all sizes and complexity. In the last couple of years, we've matured significantly, from a realtime database to a full mobile app development platform. Firebase is built on top of Google Cloud, so you get all the technical scale, enterprise-grade access control and management, and machine learning strength that underpins many of Google's products. Today, we're excited to share a few new products and features that will help you build better apps, improve your app quality, and grow your business.
Firebase has a number of products to help you engage with your users and grow your business, from machine learning powered targeting with Firebase Predictions, to app optimization without republishing via Remote Config, to re-engaging lapsed users with Cloud Messaging. Today we're adding a new product, Firebase In-App Messaging, to round out the tools available for growing your user base, as well as making improvements to a couple of our existing products.
Notifications are a great way to bring users back into your app. But how do you ensure your users are interacting with your app in the right, intended way instead of fumbling between screens without taking any meaningful action? How do you guide them through your app experience?
Today, we're launching Firebase In-App Messaging to help you guide app users who are actively using your app by sending them targeted and contextual messages. Now, you'll be able to communicate with your most valuable users - the ones already interacting with your app - and deepen engagement with them by surfacing relevant information, offers, and tips as they use your app!
Messages can be customized by format, color, and CTA, allowing you to keep your messages on brand with your app. In-App Messaging is also integrated with Google Analytics for Firebase and Firebase Predictions, making it easy for you to target messages based on user profile data like app version, current behavior like a button click, or predicted future behavior like risk of churning. In-App Messaging is starting to roll out today; check out our documentation for more detail.
The Firebase Cloud Messaging (FCM) console and APIs let you send notifications and data messages to your users on iOS, Android, and web, but understanding how your notifications are performing across all these different platforms is hard. The new FCM reporting dashboard gives teams a central place to view key messaging stats like sends, impressions, and opens, so they can easily understand how their messages are performing. In addition to aggregating all these statistics, the reporting dashboard also gives you insight into your API sends from the console, for the first time.
Developers can use this information to monitor the health of their notification functionality, such as a dip in sends after the release of a new update, as well as use insights on notification sends and opens to improve notification strategy, like monitoring how the title of a notification impacts open rates. The FCM reporting dashboard allows you to filter sends by date, platform (iOS or Android), and type (data message or notification), making it easy to find the data you're looking for.
Previously, if you wanted to check what Remote Config values you've used in the past, you had to keep track manually. In one-person teams this is a hassle, but for large teams, where lots of different developers could be changing the project's Remote Config at once, this was nearly impossible.
Today, we are happy to announce that we're adding change history to Remote Config. Firebase saves 300 versions of a project's Remote Config for up to 90 days. You'll be able to see how parameters and conditions have changed over time and if you ever want to roll back to a previous version, all you have to do is click the rollback button.
We're beginning to roll out change history for Remote Config today and it will be fully available to all projects in a couple of days. Click here to see the technical documentation.
Change history in Remote Config
When the Fabric team joined Firebase last year, we were very excited to learn from their expertise in building tools for crash reporting and debugging. Over the last 18 months, we've made big steps to make Firebase into a platform that you can use to improve your app quality, including bringing Fabric's Crashlytics to Firebase. Today, we're excited to announce a number of improvements to Crashlytics, that help it integrate better with existing tools that developer teams use.
We've heard from you that you rely on a variety of different tools to make your business successful. We want to meet you where you're already working and empower you to use the best tool for the job. That's why we're launching a couple of new integrations for Firebase Crashlytics.
First of all, you'll now be able to export your Crashlytics data from Firebase to BigQuery, allowing you to run your own analysis on deobfuscated crash reports, including any metadata such as custom keys and values, logs, and user IDs. You can then visualize data and view trends with Data Studio or any other business analytics tool you use. You'll also be able to take ownership of your data in BigQuery by setting your own retention and deletion policies.
Secondly, we're launching an integration with Jira Software that allows you to create Jira issues based on crashes reported in Firebase. Combined with the existing integration with Slack, your team can now track the crashes they are working on, with tools they already use. The Jira integration will be rolling out over the coming weeks and if you want to manage your Firebase integrations now, you can visit the settings tab in the console.
Core to Firebase's DNA are a set of products that help developers like you build mobile backend infrastructure quickly and easily. After all, it began with a Realtime Database. We've been working closely with Google Cloud Platform to build a next generation of serverless backend tools, like Cloud Firestore and Cloud Functions, and we're continuing to improve on those products. We're also launching a couple of enhancements to Firebase Hosting that we hope will help you build websites more efficiently.
A few weeks ago at Cloud Next 2018, we introduced a number of improvements to Cloud Firestore and Cloud Functions for Firebase. Cloud Firestore now lets you scale your database up to 10,000 writes per second and 1 million concurrent users to handle any surges in traffic. Cloud Functions is now GA and ready for production use -- with predictable service guaranteed by an SLA. If you're looking to build your infrastructure in certain parts of the world, both Cloud Firestore and Cloud Functions will be supporting new regions in Europe and Asia over the next few months.
Another piece of feedback we've consistently heard is that you don't always have a one-to-one relationship between Firebase projects and hosted websites. Over the next couple weeks, we'll be rolling out an improvement to Firebase Hosting that allows you to host multiple websites within one project.
Additionally, when pushing an update to a site, the Firebase CLI (from v4.1.0) now only uploads the files that have changed between releases. This dramatically speeds up the process, letting you work more efficiently.
The Project Overview page in the console has received a major update, bringing together data from all different parts of Firebase to give you a single view into the health of your app, services and business. In addition to the analytics and crash data that's always been present you can now view performance issues, notification and A/B test status, and the usage and health data for other Firebase services like Functions, Hosting, and Storage, among others.
You'll also notice that the Latest Release section of the console will now have live data. This was one of our top requested improvements to analytics in the console and we're excited to be able to provide it for you.
All of these improvements are beginning to roll out today and will be available to everybody within a couple of weeks.
Thank you, as always, for being a part of the Firebase community. We strive to create a community that is welcoming to developers of all backgrounds and companies of all levels of sophistication. Your feedback and questions are invaluable in shaping the future of Firebase, so, as always, you can find us via any of our support channels.
If you'd like to meet the team in person, please join us at the annual Firebase Summit on October 29th in Prague, Czech Republic for a day full of technical sessions, hands-on instructor-led codelabs, and another round of new updates. Hope to see you there!
Whether it's opening night for a Broadway musical or launch day for your app, both are thrilling times for everyone involved. Our agency, Posse, collaborated with Hamilton to design, build, and launch the official Hamilton app... in only three short months.
We decided to use Firebase, Google's mobile development platform, for the backend and infrastructure, while we used Flutter, a new UI toolkit for iOS and Android, for the front-end. In this post, we share how we did it.
We love to spend time designing beautiful UIs, testing new interactions, and iterating with clients, and we don't want to be distracted by setting up and maintaining servers. To stay focused on the app and our users, we implemented a full serverless architecture and made heavy use of Firebase.
A key feature of the app is the ticket lottery, which offers fans a chance to get tickets to the constantly sold-out Hamilton show. We used Cloud Functions for Firebase, and a data flow architecture we learned about at Google I/O, to coordinate the lottery workflow between the mobile app, custom business logic, and partner services.
For example, when someone enters the lottery, the app first writes data to specific nodes in Realtime Database and the database's security rules help to ensure that the data is valid. The write triggers a Cloud Function, which runs business logic and stores its result to a new node in the Realtime Database. The newly written result data is then pushed automatically to the app.
Because of Hamilton's intense fan following, we wanted to make sure that app users could get news the instant it was published. So we built a custom, web-based Content Management System (CMS) for the Hamilton team that used Firebase Realtime Database to store and retrieve data. The Realtime Database eliminated the need for a "pull to refresh" feature of the app. When new content is published via the CMS, the update is stored in Firebase Realtime Database and every app user automatically sees the update. No refresh, reload, or pull required!
Besides powering our lottery integration, Cloud Functions was also extremely valuable in the creation of user profiles, sending push notifications, and our #HamCam — a custom Hamilton selfie and photo-taking experience. Cloud Functions resized the images, saved them in Cloud Storage, and then updated the database. By taking care of the infrastructure work of storing and managing the photos, Firebase freed us up to focus on making the camera fun and full of Hamilton style.
With only three months to design and deliver the app, we knew we needed to iterate quickly on the UX and UI. Flutter's hot reload development cycle meant we could make a change in our UI code and, in about a second, see the change reflected on our simulators and phones. No rebuilding, recompiling, or multi-second pauses required! Even the state of the app was preserved between hot reloads, making it very fast for us to iterate on the UI with our designers.
We used Flutter's reactive UI framework to implement Hamilton's iconic brand with custom UI elements. Flutter's "everything is a widget" approach made it easy for us to compose custom UIs from a rich set of building blocks provided by the framework. And, because Flutter runs on both iOS and Android, we were able to spend our time creating beautiful designs instead of porting the UI.
The FlutterFire project helped us access Firebase Analytics, Firebase Authentication, and Realtime Database from the app code. And because Flutter is open source, and easy to extend, we even built a custom router library that helped us organize the app's UI code.
We enjoyed building the Hamilton app (find it on the Play Store or the App Store) in a way that allowed us to focus on our users and experiment with new app ideas and experiences. And based on our experience, we'd happily recommend serverless architectures with Firebase and customized UI designs with Flutter as powerful ways for you to save time building your app.
For us, we already have plans how to continue and develop Hamilton app in new ways, and can't wait to release those soon!
If you want to learn more about Firebase or Flutter, we recommend the Firebase docs, the Firebase channel on YouTube, and the Flutter website.
Three years ago, we launched Firebase Hosting to make it easier for developers to deliver fast, engaging experiences on the web. Two months ago, we launched the beta of Cloud Functions for Firebase to let developers write custom backend logic without having to worry about servers or infrastructure. More recently at Google I/O, we brought Firebase Hosting and Cloud Functions together to provide a flexible set of tools to build Progressive Web Apps with world-class scale and performance.
You can connect an HTTPS Cloud Function to your Firebase Hosting app by adding a rewrite to the firebase.json configuration for your project:
firebase.json
{ "hosting": { "rewrites": [ {"source": "/function/**", "function":"myFunction"} ] } }
Once connected, matching requests seamlessly proxy to your Cloud Function (in the example above, a function named myFunction). This one-line change enables exciting new capabilities for Firebase Hosting users including:
myFunction
For Cloud Functions users, you can now run functions on an SSL-secured custom domain and get powerful caching to avoid unnecessary executions.
To get started using Cloud Functions on Firebase Hosting for your own Firebase project, take a look at our documentation. You can also learn more in our I/O session: Building Fast Web Experiences with Firebase Hosting.
In apps that allow users to post public content - for instance in forums, social networks and blogging platforms - there is always a risk that inappropriate content could get published. In this post, we'll look at ways you can automatically moderate offensive content in your Firebase app using Cloud Functions.
The most commonly used strategy to moderate content is "reactive moderation". Typically, you'll add a link allowing users to report inappropriate content so that you can manually review and take down the content that does comply with your house rules. You can better prevent offensive content from being publicly visible and complement your reactive moderation by adding automated moderation mechanisms. Let's see how you can easily add automatic checks for offensive content in text and photos published by users on your Firebase apps using Cloud Functions.
We'll perform two types of automatic content moderation:
Text moderation where we'll remove swearwords and all shouting (e.g. "SHOUTING!!!").
Image moderation where we'll blur images that contain either adult or violent content.
Automatic moderation, by nature, needs to be performed in a trusted environment (i.e. not on the client), so Cloud Functions for Firebase is a great, natural fit for this. Two functions will be needed to perform the two types of moderation.
The text moderation will be performed by a Firebase Realtime Database triggered function named moderator. When a user adds a new comment or post to the Realtime Database, a function is triggered which uses the bad-words npm package to remove swear words.We'll then use the capitalize-sentence npm package to fix the case of messages that contain too many uppercase letters (which typically meaning users are shouting). The final step will be to write back the moderated message to the Realtime Database.
moderator
To illustrate this we'll use a simple data structure that represents a list of messages that have been written by users of a chat room. These are made of an object with a text attribute that gets added to the /messages list:
text
/messages
/functions-project-12345 /messages /key-123456 text: "This is my first message!" /key-123457 text: "IN THIS MESSAGE I AM SHOUTING!!!"
Once the function has run on the newly added messages, we'll add two attributes: sanitized which is true when message has been verified by our moderation function and moderated which is true if it was detected that the message contained offensive content and was modified. For instance, after the function runs on the two sample messages above we should get:
sanitized
true
moderated
/functions-project-12345 /messages /key-123456 text: "This is my first message!", sanitized: true, moderated: false /key-123457 text: "In this message I am shouting." sanitized: true, moderated: true
Our moderator function will be triggered every time there is a write to one of the messages. We set this up by using the functions.database().path('/messages/{messageId}').onWrite(...) trigger rule. We'll moderate the message and write back the moderated message into the Realtime Database:
functions.database().path('/messages/{messageId}').onWrite(...)
exports.moderator = functions.database.ref('/messages/{messageId}') .onWrite(event => { const message = event.data.val(); if (message && !message.sanitized) { // Retrieved the message values. console.log('Retrieved message content: ', message); // Run moderation checks on on the message and moderate if needed. const moderatedMessage = moderateMessage(message.text); // Update the Firebase DB with checked message. console.log('Message has been moderated. Saving to DB: ', moderatedMessage); return event.data.adminRef.update({ text: moderatedMessage, sanitized: true, moderated: message.text !== moderatedMessage }); } });
In the moderateMessage function, we'll first check if the user is shouting and if so fix the case of the sentence and then remove all bad words using the bad-words package filter.
moderateMessage
bad-words
function moderateMessage(message) { // Re-capitalize if the user is Shouting. if (isShouting(message)) { console.log('User is shouting. Fixing sentence case...'); message = stopShouting(message); } // Moderate if the user uses SwearWords. if (containsSwearwords(message)) { console.log('User is swearing. moderating...'); message = moderateSwearwords(message); } return message; } // Returns true if the string contains swearwords. function containsSwearwords(message) { return message !== badWordsFilter.clean(message); } // Hide all swearwords. e.g: Crap => ****. function moderateSwearwords(message) { return badWordsFilter.clean(message); } // Detect if the current message is shouting. i.e. there are too many Uppercase // characters or exclamation points. function isShouting(message) { return message.replace(/[^A-Z]/g, '').length > message.length / 2 || message.replace(/[^!]/g, '').length >= 3; } // Correctly capitalize the string as a sentence (e.g. uppercase after dots) // and remove exclamation points. function stopShouting(message) { return capitalizeSentence(message.toLowerCase()).replace(/!+/g, '.'); }
Note: the bad-words package uses the badwords-list package's list of swear words which only contains around 400 of these. As you know the imagination of the user out there has no limit, so this is not an exhaustive list and you might want to extend the bad words dictionary.
To moderate images we'll set up a blurOffensiveImages function that will be triggered every time a file is uploaded to Cloud Storage. We set this up by using the functions.cloud.storage().onChange(...) trigger rule. We'll check if the image contains violent or adult content using the Google Cloud Vision API. The Cloud Vision API has a feature that specifically allows to detect inappropriate content in images. Then if the image is inappropriate we'll blur the image:
blurOffensiveImages
functions.cloud.storage().onChange(...)
exports.blurOffensiveImages = functions.storage.object().onChange(event => { const object = event.data; const file = gcs.bucket(object.bucket).file(object.name); // Exit if this is a move or deletion event. if (object.resourceState === 'not_exists') { return console.log('This is a deletion event.'); } // Check the image content using the Cloud Vision API. return vision.detectSafeSearch(file).then(data => { const safeSearch = data[0]; console.log('SafeSearch results on image', safeSearch); if (safeSearch.adult || safeSearch.violence) { return blurImage(object.name, object.bucket, object.metadata); } }); });
To blur the image stored in Cloud Storage, we'll first download it locally on the Cloud Functions instance, blur the image with ImageMagick, which is installed by default on all instances, then re-upload the image to Cloud Storage:
function blurImage(filePath, bucketName, metadata) { const filePathSplit = filePath.split('/'); filePathSplit.pop(); const fileDir = filePathSplit.join('/'); const tempLocalDir = `${LOCAL_TMP_FOLDER}${fileDir}`; const tempLocalFile = `${LOCAL_TMP_FOLDER}${filePath}`; const bucket = gcs.bucket(bucketName); // Create the temp directory where the storage file will be downloaded. return mkdirp(tempLocalDir).then(() => { console.log('Temporary directory has been created', tempLocalDir); // Download file from bucket. return bucket.file(filePath).download({ destination: tempLocalFile }); }).then(() => { console.log('The file has been downloaded to', tempLocalFile); // Blur the image using ImageMagick. return exec(`convert ${tempLocalFile} -channel RGBA -blur 0x8 ${tempLocalFile}`); }).then(() => { console.log('Blurred image created at', tempLocalFile); // Uploading the Blurred image. return bucket.upload(tempLocalFile, { destination: filePath, metadata: {metadata: metadata} // Keeping custom metadata. }); }).then(() => { console.log('Blurred image uploaded to Storage at', filePath); }); }
Cloud Functions for Firebase can be a great tool to reactively and automatically apply moderation rules. Feel free to have a look at our open source samples for text moderation and image moderation.
It's been an exciting year! Last May, we expanded Firebase into our unified app platform, building on the original backend-as-a-service and adding products to help developers grow their user base, as well as test and monetize their apps. Hearing from developers like Wattpad, who built an app using Firebase in only 3 weeks, makes all the hard work worthwhile.