Backend as a Service (BaaS) companies seek to provide you, the developer, with a reliable backend solution that is quick and easy to use. With one of these services you can, in theory, save yourself the time and costs of configuring AWS servers and load balancers, or building and deploying a Ruby codebase to Heroku. However, from our experience working with Parse, one of the leading BaaS providers, we encountered a number of major gotchas that ended up making the development process take a lot longer than we estimated it would have, had we built out a Ruby backend ourselves. We hope this post will help developers realize the promise of Parse as a BaaS and avoid the efficiency killers that hampered us.
Some Backstory
Parse is the latest BaaS provider we’ve experimented with, but it is not the first. If you’re familiar with our blog, then you’ve likely seen our post on how to build An App in an Afternoon using StackMob. We liked the idea of BaaS, especially as a tool for rapidly prototyping web apps, and intended to use StackMob as part of an application to help a startup vet their idea. We had just sent our client a proposal and were out for our regular Taqueria Tuesday lunch when it was announced that PayPal was acquiring StackMob.
With the future of StackMob’s service up in the air, we had to find a new backend option. Parse had also undergone an acquisition in 2013. However, unlike StackMob which is shutting down, Parse lived on and continued to improve as one of the tools offered by Facebook for application developers. Many of the concerns we had with Parse back in mid-2012 had since been addressed (like the lack of cloud code, which was introduced that Fall). So we decided to use Parse as the backend solution.
Fast forward two months… the app is complete and in the hands of our clients. The verdict on Parse: it is a great way for a single developer to quickly build a throw-away prototype, but professional web app developers are not their current target audience. Now that might sound a little harsh, so lets take a step back and discuss some of the gotchas we encountered while developing that lead to this conclusion and that anyone considering using Parse for their backend should be aware of.
The Gotchas
No Multi-Environment Support
Parse applications have no built-in support for multiple build environments. The recommended solution is to create separate applications for your development, test, and production environments. While creating new applications in Parse is fairly simple, there is currently no way to clone applications or programmatically create them to ensure each of your “environments” has the same class structures and permissions. We ended up manually comparing and updating our environment applications whenever we wanted to ‘synchronize’ them, which took time and was prone to human error.
Security Means More Cloud Code
We had planned to use Cloud Code from the start to run cron jobs for sending emails. As we worked with Parse throughout the development process, it became clear we’d also need to use it extensively to implement some form of security. Parse allows you to configure static class level permissions through their data browser. But for anything more dynamic, such as restricting an entry to only allow the creator to update it, you need to assign a custom Access Control List (ACL) to each entry as it’s created.
ACLs can be defined by the client or in Cloud Code. While creating ACLs in client code is easier, it leaves your application vulnerable to malicious requests that were not generated by your code. So it is better to generate ACLs by adding beforeSave Cloud Code hooks to each of the classes you need to restrict. For example:
Parse.Cloud.beforeSave("YourClass", function (request, response) { var entry = request.object, creator = request.user; // The inputs to a beforeSave are not guaranteed. For // example, adding a 'YourClass' object in the Data // Browser will not have a user associated with it. // So we must check for the creator's existence first if (creator) { // beforeSave could be triggered by a create or // update. So we confirm the entry isNew before // adding an ACL. if (entry && entry.isNew()) { creator.get("accessRole").fetch().then( function (role) { var acl = new Parse.ACL(); acl.setRoleReadAccess(role, true); acl.setReadAccess(creator, true); acl.setWriteAccess(creator, true); entry.setACL(acl); response.success(); }, function (error) { console.error("Failed to fetch accessRole"); response.error(); }); } else { // This allows for updates to go through for // authenticated users response.success(); } } else { // This unfortunately blocks the data browser // from creating entries. However it also // prevents unauthenticated users from // creating them. console.error("Unauthenticated user"); console.error("...or maybe an admin?"); response.error(); } });
As the example shows, you can generate appropriate ACLs with user and role (group) access for each entry as it’s being added to the class with custom Cloud Code. One important thing to watch out for when writing these hooks is that Parse executes the beforeSave logic for both CREATE
and UPDATE
operations, but the inputs in these two situations can be very different.
Cloud Code Frustrations
Speaking of writing Cloud Code, there are several major limitations that hindered our ability to develop reliable code efficiently. There’s no developer server that allows emulating Cloud Code functionality locally, which means you have to deploy it every time you want to test any code changes. This also limits your options for debugging to logs and error messages instead of being able to use the developer tools in your browser.
This limitation also means that two developers cannot work on separate Cloud Code functions for an application at the same time. When you deploy your Cloud Code it replaces everything that was previously deployed with the files you’ve just uploaded. So if you deploy changes to test them out, any uncommitted changes your coworker was testing get replaced (even if they’re for a different function). When we asked about this at Parse’s last Bay Area meetup, the recommended solution was to have separate applications for each developer to test their Cloud Code in (please see No Multi-Environment Support above).
The last major gotcha for using Cloud Code is that it’s very difficult to unit test. We were able to write tests for a simple function by stubbing out all of the Parse Cloud Code functionality, but as soon as we started using Cloud Modules it became increasingly difficult to stub everything. Seeing as our two main use cases for Cloud Code were emailing users and security, this was a major concern for us, and is one of the biggest deterrents for using Parse in future projects that would require Cloud Code.
Nonstandard REST API
This might be less of an issue for most people depending on what frameworks you’re working with and how flexible you need your application to be. Parse provides several SDKs that do a good job of abstracting away the complications of their REST API. But for our application, we chose to forgo the JavaScript SDK and use Parse’s REST API instead. We did this because we intended to use Sencha Touch to build a mobile friendly web application. Sencha Touch’s MVC structure already has built in support for communicating with standard REST backends. So, in theory, Parse should have been simple to integrate. Also, by developing against a REST API, we gain the flexibility of being able to easily switch out Parse in the future for any other backend solution that has REST API support, without having to greatly modify the application’s codebase.
Unfortunately, as we started to implement more features and functionality, we kept encountering non-standard APIs that required custom handlers instead of relying on Sencha Touch’s default logic. For example, suppose you have a class that has a beers
field that is an array of strings. To update this field, most REST APIs support PUT
operations where you replace the contents of the field with a newly specified array. Parse instead, uses a PATCH
operation approach that allows you to modify the existing array.
{ beers: { __op:"Add", objects:[ "Ensorcelled", "Egregious" ] } }
While using PATCH isn’t incorrect, it means that if you have a model that you’ve modified, you can’t save it by performing a PUT
operation on it’s contents, but instead must track the changes separately and construct a custom patch to send to Parse. This also becomes an issue if you use Parse’s includeKeys
API parameter to hydrate pointers, as you cannot perform a PUT
operation with hydrated data.
So to that end, we wrote a lot of extra code that is Parse-specific and will have to be stripped out and cleaned up if we ever want to switch backends.
Our Conclusions
There are several other issues we encountered while developing with Parse that either took extra development time to investigate and work around, or worse, required us to compromise on the quality and security of our application to make things work. All together, these issues are enough that we do not plan on using Parse for any future client projects for now because the known costs greatly outweigh the potential gains.
However, if you are trying to rapidly prototype an application for a demo or just for fun, and you have no need for security, ease of collaboration, or multiple environments, it is possible for Parse to make the process faster. In fact, we have a new internal project that we are using Parse as the backend for those exact reasons. We have only one developer working on it, and it is a prototype that we do not plan on making secure or reusing the code for a release product.
From the sound of things at Parse’s meetup, the company’s current user base is largely native mobile-game developers. However, from talking with James Yu, Parse’s co-founder and CPO, the company is working to expand their audience:
“At Parse, we want to make it easy to use our platform in both an individual and team environment. We’re working on features that will improve development in teams, especially around Cloud Code. In fact, many of the possible improvements listed here are on our short bucket list. Stay tuned!” - James Yu, Parse Co-founder and CPO
So be sure to check their blog for feature updates before deciding whether BaaS and Parse are right for your next project.