Back

Parse Migration: How to Change Cloud Code to Be Compatible with Parse Server

Migration to Parse Server brought not only infrastructure related issues but also cloud code compatibility ones. We described challenges we faced during refactoring and prepared the detailed checklist for you.

Special thank you to our iOS Engineer Igor for his significant contributions to this post.

Bye-bye cloud Parse, hello self-hosted one!

When we started writing on Parse, we expected that at some point, we may change backend provider. We don’t perceive Parse as long-term solution, but rather as a convenient tool to get things done. We have not taken fully to the backends on Parse. Nevertheless, we now have a dozen applications that use Parse.

We build our iOS applications in such a way as that if you suddenly needed to change the backend environment, you wouldn’t significantly modify the app’s code. For this purpose, we hide a networking layer deep inside the app, covering PFObjects with our classes (on ObjC) or protocols (on Swift).

The business logic is mainly implemented in the server-side code (using the Cloud functions and Cloud jobs) rather than coded inside the app.

When Facebook announced Parse shutdown on Jan. 2017 our first intent was to migrate backend code to the Parse Server instead of writing it from scratch using another stack.

You can read about our migration adventures with Heroku in the previous post.

So we have some code, and we need to migrate it to a mostly identical environment, what could possibly go wrong? We’re going to put our backend code to another filesystem, change some file paths to have less or more resources like RAM or CPU – nothing tricky.

Soon we figured out that we were trying to write code for iOS apps ‘right’: following SOLID principles, splitting logic into modules, writing tests and other such exciting things, but our backend code was far from this. This fact complicates migration a bit :)

We described what exactly we did to make our backend code more maintainable.

You can read about Parse and Parse Server compatibility on their Github repo.

We’ve created “Parse Server Cloud Code Compatibility Checklist”Download

SOLID principles everywhere

If we had a second chance at writing backend code from scratch, I believe we would pay more attention to code decomposition, writing small, easy-to-change modules instead of putting many Cloud Functions in one file.

Following these simple principles would allow us to migrate code more easily:

  • Split your code into modules that handle only one task.
  • Keep code for API requests (aka cloud functions) and background jobs (aka cloud jobs) separate.
  • Keep code, shared between both cloud functions and jobs, wrapped into small modules and separated.

Splitting large files that contain backend code, which handles requests and business logic to the separate modules, sounds rather easy, but in the end, the question arises, “How do I avoid dropping a ball again so that changing server environment doesn’t lead to massive refactoring?”

At first it seems that cloud code itself is not a problem. Who knew that you can’t use Parse.Cloud from cloud jobs running in the Parse Server environment?

Well, Robert Martin knew, when describing SRP:

A class should have only one reason to change.

If you think about this a little more deeply, R. Martin means not only class, but every component of the system. In our case, the cloud code javascript files are components.

Read more about SOLID:

Parse Cloud Jobs

We ran into some difficulties when we were migrating Parse Cloud Jobs.

Our backend code for Cloud Functions and Cloud Jobs is separated into different files, but often Jobs need to call the same code as Functions do. In this case, we add exports to the required functions to be able to call them from the Jobs file. The easy way works fine on Parse.

Our typical backend code structure on Parse.com

Our typical backend code structure on Parse.com

But on Parse Server, you run Job as the separate entity, and you can’t use Parse.Cloud there, unless you create Parse instance. But we don’t need Parse instance to run a Job. We need to refactor our code and put shared code to a separate module.

Refactored backend code structure on Parse Server

Refactored backend code structure on Parse Server

—> Don’t create Job with too many dependencies, because now Job is not linked to your Cloud Code at all, and if some modules are used, they should be delivered with Job.

—> Put Jobs into separate files to schedule them.

—> No need to write require for your jobs in main.js, otherwise, your job may be executed when server starts

—> You need to schedule your Jobs. We used crontab for Job scheduling. It’s really easy and looks like this:

Which means that old videos are removed every 4 hours.

‘Parse’ is not available in files where you describe cloud jobs, so you can’t use code from files with API calls. Keep these things separate and independent. Read more about setting up Cloud Jobs.

Tests

All code without tests is legacy code!

We have API tests in Python, which are more like poke tests: they call Cloud functions via REST API and analyze the result. Sometimes we wrote test scenarios simulating user behavior (e.g., using Helium, SoapUI or PostMan), but had to deal with Parse authentication.

Unfortunately, poke tests cover only “public API”, the functions that are available from the app, and don’t cover internal cloud modules, because they are not very convenient to test. And of course, the code that is not covered by tests is harder to migrate.

Variable initialization

We established the rule: when you create a new entry in the database table, it’s necessary to initialize all the columns with the default values (having null as default value is ok).

Screenshot of Parse Dashboard where you can see lots of undefined values

Screenshot of Parse Dashboard where you can see lots of undefined values

Unfortunately, sometimes we forgot to initialize objects with default values (you can see in the Dashboard that some cells have undefined values). When we were trying to migrate the database to the mLab, we got some weird crashes in the iOS client inside Parse SDK when accessing undefined fields, although the same application connected to the database on Parse worked fine. We fixed those crashes by initializing undefined values.

Authentication

Of course, the user authentication was broken. Now the server code doesn’t understand what user has sent the request, and you need to grab the user’s data from the request. Sometimes you need to add additional parameters to the requests.

Our Cloud Code used Parse.current.User() a lot, until it stopped working on Parse Server, now you need to use request.user.

As for REST API tests, you need to send userId in request params, and then fetch user in your Cloud Code:

ACLs are broken too, so you may need to use sessionToken from request params. Read more about how to rewrite your Cloud Code in Parse Server compatibility guide.

Working with external modules

Parse.com supports several 3rd-party Cloud Modules to use from the server code (built-in node.js libraries). Of course, the Parse Server doesn’t include these modules. Now you need to install them separately on your server (using npm install) or include libraries as sources.

You’re lucky if you’ve added Cloud Modules as separate files: less pain with updating dependencies. Unfortunately, the code that was based on old 3rd-party modules may need to be rewritten (if the module has changed a lot).

Once again, a great Parse Server compatibility guide answers the question of which modules can be replaced by external ones, and which cannot.

Push notifications

If your app sends push notifications from the server code, this code should receive some minimal rewrites. Fortunately, Parse Server already supports push notifications! It’s only necessary to configure provision profile and certificates and to edit Cloud Code slightly.

This is how we send pushes from Cloud Code on Parse.com:

You need to change code to this on Parse Server:

Lessons learnt

It’s very useful to keep things clean in many areas: from the workplace to your code.

An attempt to migrate the code from one environment to another is better than any code review.

SOLID is the foundation of engineering culture that is applicable to any activity, not just engineering.

Give it a try. It only takes a click to unsubscribe.

Read more:

July 7, 2016

ios