Setting Up A Parse Server As An Alternative Backend

Karan Gandhi - Sep 24 '20 - - Dev Community

Sometimes, we encounter situations when it's not feasible to develop a full-fledged backend from the ground up. In such situations, we rely on services like Firebase to speed up development.

Parse is an open-source mobile Backend As a Service (mBAAS) platform. Previously, Parse was a backend platform similar to Firebase. In 2016, Facebook open-sourced the platform code and sunsetted their hosting services. In this article, we will be taking a brief look at Parse and seeing how it allows us to deploy applications quickly.

Overview

Parse is a full-fledged backend with support for REST API and GraphQL which can be self-hosted. It is open source and is being actively maintained by communities.

It has SDKs available for iOS, Android, JavaScript, and other platforms, as well as support for Push Notifications including campaigns, out-of-the-box user management, support for OAuth providers (including Facebook, Twitter, Google, GitHub, and LDAP), support for Docker, deployment options to various platforms (including AWS and Heroku), as well as support for different storage adapters.

Parse is extensible with webhooks, jobs, and config, and has quite a few community plugins. You can use it together with Express.js.

Setting up Parse

Parse requires Node 8+, MongoDB or PostgreSQL to set up. On UNIX like systems, it's advisable to use NVM for Node.js installations. The steps are as follows:

npm install -g parse-server mongodb-runner
mongodb-runner start
parse-server --appId APPLICATION_ID --masterKey MASTER_KEY --databaseURI mongodb://localhost/test
Enter fullscreen mode Exit fullscreen mode

We can also use Docker to start Parse:

git clone https://github.com/parse-community/parse-server
cd parse-server
docker build --tag parse-server .
docker run --name my-mongo -d mongo
docker run --name my-parse-server -v cloud-code-vol:/parse-server/cloud -v config-vol:/parse-server/config -p 1337:1337 --link my-mongo:mongo -d parse-server --appId APPLICATION_ID --masterKey MASTER_KEY --databaseURI mongodb://mongo/test
Enter fullscreen mode Exit fullscreen mode

APPLICATION_ID is the name of the application and can be user-generated. MASTER_KEY is a key that can override all permissions. Other parameters are listed below:

  • databaseURI: Connection string URI for your MongoDB.
  • cloud: Path to your app’s Cloud Code.
  • appId: A unique identifier for your app.
  • fileKey: A key that specifies a prefix used for file storage. For migrated apps, this is necessary to provide access to files already hosted on Parse.
  • masterKey: A key that overrides all permissions. Keep this secret.
  • clientKey: The client key for your app. (optional)
  • restAPIKey: The REST API key for your app. (optional)
  • javascriptKey: The JavaScript key for your app. (optional)
  • dotNetKey: The .NET key for your app. (optional)
  • push: An object containing push configuration. See Push
  • filesAdapter: An object that implements the FilesAdapter interface. For example, the S3 files adapter
  • auth: Configure support for 3rd party authentication.
  • maxUploadSize: Maximum file upload size.

And that's it! We have a Parse backend running successfully in minutes.

For convenience, we can install the Parse dashboard - a visual admin panel for the Parse server. To run parse-dashboard, we need to install it globally.

npm -i g parse-dashboard

parse-dashboard --dev --appId APPLICATION_ID --masterKey MASTER_KEY --serverURL "http://localhost:1337/parse/" --appName SimpleFileStorage
Enter fullscreen mode Exit fullscreen mode

We can access the dashboard on localhost:4040. For this tutorial, we will be using the REST API.

Backend Features

Storing Data

As mentioned earlier, Parse allows us to store data easily. The base unit of all data is the objects API. For example, if we define a vehicle class with the keys manufacturer and model, we can perform CRUD REST operations using a simple curl request.

curl -X POST \
  -H "X-Parse-Application-Id: simple_file_storage" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"manufacturer": "Lamborghini", "model":  "Gallardo"}' \
  http://localhost:1337/parse/classes/vehicle

Enter fullscreen mode Exit fullscreen mode

We get objectId and created _date in response. Further operations on the object can be performed using objectid.

curl -X GET \
  -H "X-Parse-Application-Id: simple_file_storage" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "Content-Type: application/json" \
  http://localhost:1337/parse/classes/vehicle/objectId
Enter fullscreen mode Exit fullscreen mode
curl -X PUT \
  -H "X-Parse-Application-Id: simple_file_storage" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"manufacturer": "Lamborghini", "model":  "Murcielago"}' \
  http://localhost:1337/parse/classes/vehicle/objectId
Enter fullscreen mode Exit fullscreen mode
curl -X DELETE \
  -H "X-Parse-Application-Id: simple_file_storage" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  http://localhost:1337/parse/classes/objectId

Enter fullscreen mode Exit fullscreen mode

In case a class is not predefined, it will be created by the server. We can use Parse Dashboard to create custom classes. As far as data types are concerned, Parse has support for string, number, boolean, arrays, JSON Objects, Date-Time, File, and Null. Additionally Parse has two custom datatypes, Pointer to Another Parse Object and Relation to another Parse Class. Data types in Parse are locked in. Once a datatype is set, it will return an error if you try to save anything else.

Files

As with data, file upload is straightforward. The files URL is the /files route and the file name.
However, we have to manually handle the content type.

curl -X POST \
  -H "X-Parse-Application-Id: simple_file_storage" \
  -H "X-Parse-REST-API-Key: REST_API_KEY" \
  -H "Content-Type: image/jpeg" \
  --data-binary '@myPicture.jpg' \
  http://localhost:1337/parse/files/pic.jpg
Enter fullscreen mode Exit fullscreen mode

As a response, we receive the file location and name of the saved file. A unique identifier is appended to the file name.

{
  "url": "http://localhost:1337/parse/files/simple_file_storage/d840137c22d89d126075ec7fa875c54f_pic.jpg",
  "name": "d840137c22d89d126075ec7fa875c54f_pic.jpg"
}

Enter fullscreen mode Exit fullscreen mode

Authentication & Security

Parse provides out-of-the-box user authentication. This includes token-based authentication, user actions like sign up, login, email verification, reset passwords, token-based session management, and role-based access management. The routes for users are /parse/users, the routes for roles are /parse/roles and the routes for sessions are /parse/sessions.

To secure data, Parse provides Class Level Permissions (CLP) and Access Control Lists (ACL). CLP provides fine-grained control over data access to roles. Using Class Level Permissions we can define Roles which will have the ability to:

  • Create new Classes;
  • Add Fields to Classes;
  • Read or Query Data from classes;

Furthermore, using ACLs we can restrict access to objects to individuals and roles. An example snippet of ACL is:

{
  classLevelPermissions:
  {
    "find": {
      "requiresAuthentication": true,
      "role:admin": true
    },
    "get": {
      "requiresAuthentication": true,
      "role:admin": true
    },
    "create": { "role:admin": true },
    "update": { "role:admin": true },
    "delete": { "role:admin": true }
  }
}
Enter fullscreen mode Exit fullscreen mode

This particular object:

  • is inaccessible to guests;
  • requires authentication to view and query objects;
  • has a predefined role admin, can perform all operations.

The picture below, from the Parse docs, demonstrates how CLPs and ACLs interact.

clp_vs_acl_diagram

Miscellaneous Features

  • Cloud Functions allow us to define custom functions in the Parse backend.
  • Hooks allow us to run custom code in other languages and extend server-side logic.
  • Jobs allow us to run long-running functions so we don't have to wait for a response.
  • Triggers allow us to write custom code before/after data is modified.
  • Analytics allows us to add dimensions and metrics to our application.
  • Push Dashboard allows us to create custom push campaigns for our mobile apps.
  • Geo points allow us to associate real-world latitudes and longitudes with an object.
  • Config allows us to save config parameters on the server.

Extending Parse with Chisel and Parse Auditor

Chisel CMS

Chisel is an API-first headless CMS built upon Parse. Setting up Chisel over Parse is quite straightforward.

npm install -g chisel-cms
chisel-cms --appId "APP_ID" --serverURL "https://YOURSERVER.com/parse"
Enter fullscreen mode Exit fullscreen mode

Chisel will start at localhost:9000.

Chisel provides an admin panel where we can set up multiple sites. Additionally, it has its own Parse server. While you can run Chisel on any Parse server, Chisel's server has helpful templates like blog and knowledge base.
It's advisable to check the data structures and content publishing life cycle before diving in.

Parse Auditor

Parse auditor is a module inspired by the Envers Project. It adds automated data tracking/auditing to classes. This is useful when the app is needed to adhere to regulations like HIPAA. Parse Auditor has to be used in the cloud code. Let's take a look at how Parse Auditor works

Let's assume we have ImportantData and SortOfImportanData classes in Parse. We would like to track data changes in both classes and know if ImportantData was accessed.

To set up Parse Auditor, we need to edit the cloud code. First, shut down any running instance of the Parse server and dashboard. Then, navigate to the cloud folder of your Parse installation. If there is no cloud folder, then create a main.js file inside a cloud folder. We need the absolute path of the cloud folder. It should look like this:

C:\somefolder\Parse\cloud\main.js
Enter fullscreen mode Exit fullscreen mode

Inside the cloud folder, add parse-auditor to the dependencies in package.json

{
  "dependencies": {
    "parse-auditor": "*"
  }
}

Enter fullscreen mode Exit fullscreen mode

Now, edit the main.js file as follows:

const ParseAuditor = require('parse-auditor');
ParseAuditor(['ImportantData', 'SortOfImportantData'],['ImportantData'])
Enter fullscreen mode Exit fullscreen mode

The first param takes an array of Class names and tracks them in a separate Class_AUD class. The second param takes an array of Class names and tracks views in the Class_AUD class.

By default, changes to ImportantData and SortOfImportantData will be tracked in ImportantData_AUD and SortOfImportantData_AUD. Views of ImportantData will be tracked in SortOfImportantData_AUD. Four extra fields are attached to audit logs:

  • meta_actor: The user involved in this event. Either the user who made the update or the one who viewed this record.
  • meta_action: Will be "SAVE", "DELETE", or "FIND" depending on the action the user took.
  • meta_class: The name of the class, convenient when combining complex audit histories across many classes.
  • meta_subject: The row being edited/viewed.

The plugin can be configured further using these fields:

{
    classPrefix: '', //Class Prefix
    classPostfix: '_AUD', //Class Postfix
    fieldPrefix: 'meta_', //field Postfix
    fieldPostfix: '', // field Postfix
    parseSDK: Parse, //SDK Object
    useMasterKey: false, //MasterKey
    clp: {} //Class Level Permissions 
}
Enter fullscreen mode Exit fullscreen mode

Consider the example below:

const ParseAuditor = require('parse-auditor');
const customConfig = { classPostfix: '_LOGS' };
ParseAuditor(['ImportantData', 'SortOfImportantData'], ['ImportantData'], customConfig);
Enter fullscreen mode Exit fullscreen mode

This will log the data to a class sufficed with _LOGS. To start the server, we will have to pass main.js to the cloud param as below.

parse-server --appId APPLICATION_ID --masterKey MASTER_KEY --databaseURI mongodb://localhost/test --cloud "C:\somefolder\Parse\cloud\main.js"
Enter fullscreen mode Exit fullscreen mode

With this, we have successfully set up Parse with custom logging.

Final Thoughts

In this article, we have seen how we quickly generate an app backend using Parse.

We have covered how Parse handles permissions and how they can be used to secure data. Also, we have covered two useful tools that can be used with Parse: Chisel CMS and Parse Auditor.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .