Skip to content

Configuring a Parse Server

Guillermo Sanchez edited this page Sep 16, 2020 · 108 revisions

1. Overview

Parse provides a cloud-based backend service to build data-driven mobile apps quickly. Facebook, which acquired the company in 2013, shut down service on January 28, 2017. An open source version enables developers to continue using their apps was published, along with a migration guide.

While there are many alternate options to Parse, most of them lack either the functionality, documentation, or sample code to enable quick prototyping. For this reason, the open source Parse version is a good option to use with minimal deployment/configuration needed.

1.1 Differences with Open Source Parse

You can review this Wiki to understand the current development progress of this app. There are a few notable differences in the open source version:

  • Authentication: By default, only an application ID is needed to authenticate with open source Parse. The base configuration that comes with the one-click deploy options does not require authenticating with any other types of keys. Therefore, specifying client keys on Android or iOS is not needed.

  • Push notifications: Because of the implicit security issues with allowing push notifications to be sent through Android or iOS directly to other devices, this feature is disabled. Normally in Parse.com you can toggle an option to override this security restriction. For open source Parse, you must implement pre-defined code written in JavaScript that can be called by the clients to execute, otherwise known as Parse Cloud.

  • Single app aware: The current version only supports single app instances. There is ongoing work to make this version multi-app aware. However, if you intend to run many different apps with different datastores, you currently would need to instantiate separate instances.

  • File upload limitations: The backend for open source is backed by MongoDB, and the default storage layer relies on Mongo's GridFS layer. The current limit is set for 20 MB but you depend on storing large files, you should really configure the server to use Amazon's Simple Storage Service (S3).

Many of the options need to be configured by tweaking your own configuration. You may wish to fork the code that helps instantiate a Parse server and change them based on your own needs.

2. Setting a new Parse Server

We will be using back4app.com to host our Parse dashboard. Below are the steps for deploying your Parse server on back4app.

If you are interested in hosting your Parse dashboard in another platform (i.e. AWS, Azure, Google'S GCP, etc.), check out this guide. NOTE: Most other hosts require a credit card to get started with a Parse dashboard.

Sign up to back4app

Back4app is a platform that helps minimizing the workload of setting up the backend. Basically, they set up all the backend for you. Due to the nature of our course, we will mainly focus on how to connect an iOS app to your own backend server rather than teach the nitty gritties of creating a backend from scratch.

  1. Sign Up for Back4app
  2. Create a new Parse App

  1. Save your config variables (located on Core settings):

  • Leave PARSE_MOUNT to be /parse. It does not need to be changed.
  • Set APP_ID for the app identifier. If you do not set one, the default is set as myAppId. You will need this info for the Client SDK setup.
  • Set MASTER_KEY to be the master key used to read/write all data. You will only use this key if you intend to setup the Parse Dashboard.
  • Set SERVER_URL to correspond to match the App Name you defined in step #2 along with the PARSE_MOUNT (e.g. https://yourappname.herokuapp.com/parse)
  • If you intend to use Parse's Facebook authentication, set FACEBOOK_APP_ID to be the FB application ID.
  1. ^^Using the config variables, connect your app:

    // AppDelegate.swift
    
    
    // Don't forget to install Parse pods!
    import Parse
    
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) {
            let parseConfig = ParseClientConfiguration {
                $0.applicationId = "parseAppId"
                $0.clientKey = "parseClientKey"
                $0.server = "parseServerUrlString"
            }
            Parse.initialize(with: parseConfig)
            return true
    }

You can change the core setting values to whatever you would like!

3. Browsing Parse Data

What's also good about Back4app.com is that it provides the dashboard for you so you can view the Parse data. Here is a screenshot of where your Parse data is stored:

4. Add Parse client to an Xcode project

  1. Create a Podfile file:

    pod init
    
  2. Add dependencies in your Podfile (Don't forget to save your Podfile):

    # Uncomment the next line to define a global platform for your project
    # platform :ios, '9.0'
    
    target 'YOUR_APP' do
      # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
      use_frameworks!
    
      # Pods for YOUR_APP
      pod 'Parse'
    
      ...
  3. Install the new pods:

    pod install
  4. Initialize Parse in your AppDelegate to point to your own server:

    // AppDelegate.swift
    
    // ...
    
    import Parse
    
    class AppDelegate: UIResponder, UIApplicationDelegate {
    
        // ...
    
        func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    
            // Initialize Parse
            // Set applicationId and server based on the values in the Heroku settings.
            Parse.initialize(
                with: ParseClientConfiguration(block: { (configuration: ParseMutableClientConfiguration) -> Void in
                    configuration.applicationId = "myAppId"
                    configuration.server = "https://myAppName.herokuapp.com/parse"
                })
            )
    #import "AppDelegate.h"
    #import "Parse/Parse.h"
    
    @interface AppDelegate ()
    
    @end
    
    @implementation AppDelegate
    
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(  NSDictionary *)launchOptions {
        
        ParseClientConfiguration *config = [ParseClientConfiguration   configurationWithBlock:^(id<ParseMutableClientConfiguration> configuration) {
            
            configuration.applicationId = @"codepathInstagram";
            configuration.server = @"http://codepathfbinstagram.herokuapp.com/parse";
        }];
        
        [Parse initializeWithConfiguration:config];
        
        return YES;
    }

The /parse path needs to match the PARSE_MOUNT environment variable, which is set to this value by default.

5. Adding Support for Live Queries

One of the newer features of Parse is that you can monitor for live changes made to objects in your database (i.e. creation, updates, and deletes) To get started, make sure you have defined the ParseObjects that you want in your NodeJS server. Make sure to define a list of all the objects by declaring it in the liveQuery and classNames listing:

let api = new ParseServer({
  ...,
  // Make sure to define liveQuery AND classNames
  liveQuery: {
    // define your ParseObject names here
    classNames: ['Post', 'Comment']
  }
});

See this guide and this spec for more details. Parse Live Queries rely on the websocket protocol, which creates a bidirectional channel between the client and server and periodically exchange ping/pong frames to validate the connection is still alive.

Websocket URLs are usually prefixed with ws:// or wss:// (secure) URLs. Heroku instances already provide websocket support, but if you are deploying to a different server (Amazon), you may need to make sure that TCP port 80 or TCP port 443 are available.

6. Troubleshooting

  • If you see Application Error or An error occurred in the application and your page could not be served. Please try again in a few moments., double-check that you set a MASTER_KEY in the environment settings for that app.

  • If you are using Heroku, download the Heroku Toolbelt app here to help view system logs.

    First, you must login with your Heroku login and password:

    heroku login

    You can then view the system logs by specifying the app name:

    heroku logs --app <app name>

    The logs should show the response from any types of network requests made to the site. Check the status code.

    2016-02-07T08:28:14.292475+00:00 heroku[router]: at=info method=POST path="/parse/classes/Message" host=parse-testing-port.herokuapp.com request_id=804c2533-ac56-4107-ad05-962d287537e9 fwd="101.12.34.12" dyno=web.1 connect=1ms service=2ms status=404 bytes=179
    

7. Enabling Push Notifications

  1. Create an auth token through developer.apple.com by clicking on the Keys -> All section. Fill out a name and make sure the APNS service is checked: Save the .p8 file and record the key ID. You will need to add this information to your Parse configuration.

  2. Fork your own copy of the Parse server code that initially used to deploy to Heroku. You will need to reconfigure your Heroku instance to point to this repo instead of Parse's because of additional customizations needed to be made on the index.js file within this repo.

  3. Copy the .p8 certificate you exported and add it to this forked repo. This .p8 file should not have a passphrase with it.

  4. You will now need to edit the index.js to include to the APNS certificate. You will need to specify the key ID, team ID, and the location of this p8 certificate. ```javascript var authKeyPath = path.resolve(__dirname, 'AuthKey.p8');

    var pushConfig = {'ios': { token: {
       key: authKeyPath, // P8 file only
       keyId: 'XXXXX', // key ID
       teamId: 'YYYYY', // The Team ID of your Apple Developer Account (available at https://developer.apple.com/account/#/membership/)
      },
      production: false // set explicitly
     }
    };
    ```
    

    The Parse server relies on the node-apn module for sending Apple push notifications. See this guide for more information about iOS push options.

  5. Make sure to include this pushConfig into your definition:

    ```javascript
        var api = new ParseServer({
        .
        .
        push: pushConfig,
        });
    ```
    
  6. Follow client steps to enable Push notifications inside your app.

    • Make sure to use the same bundle identifier as the name specified in your server configuration.
    • Verify that you've turned on Push Notifications in the Capabilities section.
    • Click on Build Setting", and find (or search for) the Code Signing Identity field. This field should be set to iOS Developer if you're testing against development, or iOS Distribution if you're testing in production or building your app for the App Store.
  7. Make sure to register your application for push notifications. First, you should specify inside AppDelegate.swift the notification types to which the app will respond:

    ```swift
    // Swift
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
     {
       ...
       let userNotificationTypes: UIUserNotificationType = [.Alert, .Badge, .Sound]
       let settings = UIUserNotificationSettings(forTypes: userNotificationTypes, categories: nil)
       application.registerUserNotificationSettings(settings)
       application.registerForRemoteNotifications()
        ...
     }
    ```
    
  8. Next, the application:didRegisterForRemoteNotificationsWithDeviceToken: will be called if registration is successful. The response comes with a device token which we want to pass along to the server.

    func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
        let installation = PFInstallation.currentInstallation()
        installation.setDeviceTokenFromData(deviceToken)
        installation.channels = ["global"]
        installation.saveInBackground()
    }
  9. Test out whether you can receive push notifications by using this Curl command:

    curl -X POST \
    -H "X-Parse-Application-Id: myAppId" \
    -H "X-Parse-Master-Key: masterKey" \
    -H "Content-Type: application/json" \
    -d '{
          "where": {
            "deviceType": "ios"
          },
          "data": {
            "title": "The Shining",
            "alert": "All work and no play makes Jack a dull boy."
          }
        }'\   http://yourherouapp.herokuapp.com/parse/push

    You should see inside your logs:

    APNS Connection 0 Connected
    APNS Connection 0 Notification transmitted to <device_token>
    

NOTE: Apple has two separate push notification environments for production and development purposes. Whether your push tokens are granted for development or production purposes depends on the certificate used to build your app. If the app was signed using an App Store certificate, it is designated for production. Otherwise, in most other cases, the token is relying on the development environment.

If you are building and testing your app in XCode (note that push notifications cannot be tested on an emulator), you will likely be testing in the development environment. Once your app is distributed through the app store, you will need to setup the Parse server to be rely on the production: true setting. You will most likely need to have separate Parse servers, one setup for production and the other setup for development purposes.

Sending Pushes from Clients

While support for push notifications is now available with the open source Parse server, you cannot implement this type of code on the actual client:

   // Note: This does NOT work with open Parse Server at this time
   let push = PFPush.init()
   push.setChannel("mychannel")
   push.setMessage("this is my message")
   push.sendPushInBackground()

You will likely see this error in the API response:

unauthorized: master key is required (Code: 0, Version: 1.12.0)

Instead, you need to write your own server-side Parse code and have the client invoke it.

Verify that cloud/main.js is the default value of CLOUD_CODE_MAIN environment variable. You should modify your cloud/main.js file to define this Parse Cloud function:

// iOS push testing
Parse.Cloud.define("iosPushTest", function(request, response) {

  // request has 2 parameters: params passed by the client and the authorized user                                                                                                                               
  var params = request.params;
  var user = request.user;

  // Our "Message" class has a "text" key with the body of the message itself                                                                                                                                    
  var messageText = params.text;

  var pushQuery = new Parse.Query(Parse.Installation);
  pushQuery.equalTo('deviceType', 'ios'); // targeting iOS devices only                                                                                                                                          

  Parse.Push.send({
    where: pushQuery, // Set our Installation query                                                                                                                                                              
    data: {
      alert: "Message: " + messageText
    }
  }, { success: function() {
      console.log("#### PUSH OK");
  }, error: function(error) {
      console.log("#### PUSH ERROR" + error.message);
  }, useMasterKey: true});

  response.success('success');
});

Make sure to redeploy your code with these changes to Heroku first. Then you can use the client to test:

curl -X POST \
-H "X-Parse-Application-Id: myAppId" \
-H "X-Parse-Master-Key: masterKey" \
-H "Content-Type: application/json" \
-d '{
      "where": {
        "deviceType": "ios"
      },
      "text": "This is a test"
    }' \
http://myherokuapp.herokuapp.com/parse/functions/iosPushTest

You should receive a {"result":"success"} message back if your application ID and masterKey matches your configuration.

You can then invoke this function inside your iOS client by adding the following command. Note how the text parameter is used for the message to be sent:

PFCloud.callFunctionInBackground("iosPushTest", withParameters: ["text" : "Testing"])

Troubleshooting

  • Query the Installation table and make sure your app has registered a device token.

    curl -X GET \
         -H "X-Parse-Application-Id: myAppId" \
         -H "X-Parse-Master-Key: masterKey" \
         http://myappname.herokuapp.com/parse/installations | python -mjson.tool

    You should see:

      {
              "appIdentifier": "beta.codepath.pushtest",
              "appName": "pushtest",
              "appVersion": "1",
              "badge": 0,
              "channels": [
                  "global"
              ],
              "createdAt": "2016-03-13T07:07:12.184Z",
              "deviceToken": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
              "deviceType": "ios",
              "installationId": "498afd36-1987-4762-a5a3-9b958de41089",
              "localeIdentifier": "en-US",
              "objectId": "E6RQBu4q3e",
              "parseVersion": "1.12.0",
              "timeZone": "America/Los_Angeles",
              "updatedAt": "2016-03-13T07:07:12.184Z"
          }
    
  • Make sure your bundle ID matches what you specified in your index.js. If you get Invalid Token responses, it means that you may have a mismatch issue.

  • If you are using a development certificate, make sure it is marked as production: false in your Parse server configuration.

  • Verify you can connect to Apple's APNS service by following these instructions.

  • Enable network logging on your IOS client by reviewing this Parse guide.

Clone this wiki locally