Cloud Code

What is Cloud Code?

Parse's vision is to let developers build any mobile app without dealing with servers. For complex apps, sometimes you just need a bit of logic that isn't running on a mobile device. Cloud Code makes this possible.

Cloud Code is easy to use because it's built on the same JavaScript SDK that powers thousands of apps. The only difference is that this code runs in the Parse Cloud rather than running on a mobile device. When you update your Cloud Code, it becomes available to all mobile environments instantly. You don't have to wait for a new release of your application. This lets you change app behavior on the fly and add new features faster.

Even if you're only familiar with mobile development, we hope you'll find Cloud Code straightforward and easy to use.

What Cloud Code Is Not

Cloud Code is not Node.js. Both Cloud Code and Node.js are built on the V8 JavaScript engine, but similarities end there. If you're interested in using Node.js with Parse, you can read more about using Heroku with Parse on our blog.

Getting Started

On the computer you use for development, you will need to install Parse's command line tool. This will let you manage your code in the Parse Cloud. Visit the Installation section of the Command Line Tool guide to learn how to install the tool, set up your Cloud Code directory, and deploy your code.

Cloud Functions

Let's look at a slightly more complex example where Cloud Code is useful. One reason to do computation in the cloud is so that you don't have to send a huge list of objects down to a device if you only want a little bit of information. For example, let's say you're writing an app that lets people review movies. A single Review object could look like:

{
  "movie": "The Matrix",
  "stars": 5,
  "comment": "Too bad they never made any sequels."
}

If you wanted to find the average number of stars for The Matrix, you could query for all of the reviews, and average the stars on the device. However, this uses a lot of bandwidth when you only need a single number. With Cloud Code, we can just pass up the name of the movie, and return the average star rating.

Cloud functions accept a JSON parameters dictionary on the request object, so we can use that to pass up the movie name. The entire Parse JavaScript SDK is available in the cloud environment, so we can use that to query over Review objects. Together, the code to implement averageStars looks like:

Parse.Cloud.define("averageStars", function(request, response) {
  var query = new Parse.Query("Review");
  query.equalTo("movie", request.params.movie);
  query.find({
    success: function(results) {
      var sum = 0;
      for (var i = 0; i < results.length; ++i) {
        sum += results[i].get("stars");
      }
      response.success(sum / results.length);
    },
    error: function() {
      response.error("movie lookup failed");
    }
  });
});

The only difference between using averageStars and hello is that we have to provide the parameter that will be accessed in request.params.movie when we call the Cloud function. Read on to learn more about how Cloud functions can be called.

Cloud functions can be called from any of the client SDKs, as well as through the REST API. For example, to call the Cloud function named averageStars with a parameter named movie from an Android app:

HashMap<String, Object> params = new HashMap<String, Object>();
params.put("movie", "The Matrix");
ParseCloud.callFunctionInBackground("averageStars", params, new FunctionCallback<Float>() {
   void done(Float ratings, ParseException e) {
       if (e == null) {
          // ratings is 4.5
       }
   }
});

To call the same Cloud function from an iOS app:

// Objective-C
[PFCloud callFunctionInBackground:@"averageStars"
                   withParameters:@{@"movie": @"The Matrix"}
                            block:^(NSNumber *ratings, NSError *error) {
  if (!error) {
     // ratings is 4.5
  }
}];
// Swift
PFCloud.callFunctionInBackground("averageRatings", withParameters: ["movie":"The Matrix"]) {
  (response: AnyObject?, error: NSError?) -> Void in
  let ratings = response as? Float
  // ratings is 4.5
}

This is how you would call the same Cloud function using PHP:

$ratings = ParseCloud::run("averageRatings", ["movie" => "The Matrix"]);
// $ratings is 4.5

The following example shows how you can call the "averageRatings" Cloud function from a .NET C# app such as in the case of Windows 10, Unity, and Xamarin applications:

IDictionary<string, object> params = new Dictionary<string, object>
{
    { "movie", "The Matrix" }
};
ParseCloud.CallFunctionAsync<IDictionary<string, object>>("averageStars", params).ContinueWith(t => {
  var ratings = t.Result;
  // ratings is 4.5
});

You can also call Cloud functions using the REST API:

curl -X POST \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{ "movie": "The Matrix" }' \
  https://api.parse.com/1/functions/averageStars

And finally, to call the same function from a JavaScript app:

Parse.Cloud.run('averageStars', { movie: 'The Matrix' }).then(function(ratings) {
  // ratings should be 4.5
});

In general, two arguments will be passed into cloud functions:

  1. request - The request object contains information about the request. The following fields are set:
    1. params - The parameters object sent to the function by the client.
    2. user - The Parse.User that is making the request. This will not be set if there was no logged-in user.

If the function is successful, the response in the client looks like:

{ "result": 4.8 }

If there is an error, the response in the client looks like:

{
  "code": 141,
  "error": "movie lookup failed"
}

beforeSave Triggers

Implementing validation

Another reason to run code in the cloud is to enforce a particular data format. For example, you might have both an Android and an iOS app, and you want to validate data for each of those. Rather than writing code once for each client environment, you can write it just once with Cloud Code.

Let's take a look at our movie review example. When you're choosing how many stars to give something, you can typically only give 1, 2, 3, 4, or 5 stars. You can't give -6 stars or 1337 stars in a review. If we want to reject reviews that are out of bounds, we can do this with the beforeSave method:

Parse.Cloud.beforeSave("Review", function(request, response) {
  if (request.object.get("stars") < 1) {
    response.error("you cannot give less than one star");
  } else if (request.object.get("stars") > 5) {
    response.error("you cannot give more than five stars");
  } else {
    response.success();
  }
});

If response.error is called, the Review object will not be saved, and the client will get an error. If response.success is called, the object will be saved normally. Your code should call one of these two callbacks.

One useful tip is that even if your mobile app has many different versions, the same version of Cloud Code applies to all of them. Thus, if you launch an application that doesn't correctly check the validity of input data, you can still fix this problem by adding a validation with beforeSave.

If you want to use beforeSave for a predefined class in the Parse JavaScript SDK (e.g. Parse.User), you should not pass a String for the first argument. Instead, you should pass the class itself:

Parse.Cloud.beforeSave(Parse.User, function(request, response) {
  if (!request.object.get("email")) {
    response.error("email is required for signup");
  } else {
    response.success();
  }
});

Modifying Objects on Save

In some cases, you don't want to throw out invalid data. You just want to tweak it a bit before saving it. beforeSave can handle this case, too. You just call response.success on the altered object.

In our movie review example, we might want to ensure that comments aren't too long. A single long comment might be tricky to display. We can use beforeSave to truncate the comment field to 140 characters:

Parse.Cloud.beforeSave("Review", function(request, response) {
  var comment = request.object.get("comment");
  if (comment.length > 140) {
    // Truncate and add a ...
    request.object.set("comment", comment.substring(0, 137) + "...");
  }
  response.success();
});

afterSave Triggers

In some cases, you may want to perform some action, such as a push, after an object has been saved. You can do this by registering a handler with the afterSave method. For example, suppose you want to keep track of the number of comments on a blog post. You can do that by writing a function like this:

Parse.Cloud.afterSave("Comment", function(request) {
  query = new Parse.Query("Post");
  query.get(request.object.get("post").id, {
    success: function(post) {
      post.increment("comments");
      post.save();
    },
    error: function(error) {
      console.error("Got an error " + error.code + " : " + error.message);
    }
  });
});

The client will receive a successful response to the save request after the handler terminates, regardless of how it terminates. For instance, the client will receive a successful response even if the handler throws an exception. Any errors that occurred while running the handler can be found in the Cloud Code log.

If you want to use afterSave for a predefined class in the Parse JavaScript SDK (e.g. Parse.User), you should not pass a String for the first argument. Instead, you should pass the class itself.

beforeDelete Triggers

You can run custom Cloud Code before an object is deleted. You can do this with the beforeDelete method. For instance, this can be used to implement a restricted delete policy that is more sophisticated than what can be expressed through ACLs. For example, suppose you have a photo album app, where many photos are associated with each album, and you want to prevent the user from deleting an album if it still has a photo in it. You can do that by writing a function like this:

Parse.Cloud.beforeDelete("Album", function(request, response) {
  query = new Parse.Query("Photo");
  query.equalTo("album", request.object.id);
  query.count({
    success: function(count) {
      if (count > 0) {
        response.error("Can't delete album if it still has photos.");
      } else {
        response.success();
      }
    },
    error: function(error) {
      response.error("Error " + error.code + " : " + error.message + " when getting photo count.");
    }
  });
});

If response.error is called, the Album object will not be deleted, and the client will get an error. If response.success is called, the object will be deleted normally. Your code should call one of these two callbacks.

If you want to use beforeDelete for a predefined class in the Parse JavaScript SDK (e.g. Parse.User), you should not pass a String for the first argument. Instead, you should pass the class itself.

afterDelete Triggers

In some cases, you may want to perform some action, such as a push, after an object has been deleted. You can do this by registering a handler with the afterDelete method. For example, suppose that after deleting a blog post, you also want to delete all associated comments. You can do that by writing a function like this:

Parse.Cloud.afterDelete("Post", function(request) {
  query = new Parse.Query("Comment");
  query.equalTo("post", request.object);
  query.find({
    success: function(comments) {
      Parse.Object.destroyAll(comments, {
        success: function() {},
        error: function(error) {
          console.error("Error deleting related comments " + error.code + ": " + error.message);
        }
      });
    },
    error: function(error) {
      console.error("Error finding related comments " + error.code + ": " + error.message);
    }
  });
});

The afterDelete handler can access the object that was deleted through request.object. This object is fully fetched, but cannot be refetched or resaved.

The client will receive a successful response to the delete request after the handler terminates, regardless of how it terminates. For instance, the client will receive a successful response even if the handler throws an exception. Any errors that occurred while running the handler can be found in the Cloud Code log.

If you want to use afterDelete for a predefined class in the Parse JavaScript SDK (e.g. Parse.User), you should not pass a String for the first argument. Instead, you should pass the class itself.

Resource Limits

Timeouts

Cloud functions will be killed after 15 seconds of wall clock time. beforeSave, afterSave, beforeDelete, and afterDelete functions will be killed after 3 seconds of run time. If a Cloud function or a beforeSave/afterSave/beforeDelete/afterDelete function is called from another Cloud Code call, it will be further limited by the time left in the calling function. For example, if a beforeSave function is triggered by a cloud function after it has run for 13 seconds, the beforeSave function will only have 2 seconds to run, rather than the normal 3 seconds. If you need additional time to perform operations in Cloud Code, consider using a background job.

Memory

Cloud Code invocations (such as functions, save/delete hooks, background jobs, and custom endpoint handlers) must use a finite amount of memory (currently 384 MB, but subject to change in the future). This memory is garbage collected by the JavaScript engine, so it is generally not an issue.

To allow the garbage collector to do its work properly, make sure that you aren't saving references to large objects, HTTP response buffers, and other objects to global variables in your script.

Network requests

Network requests that are still in progress after success or error are called will be canceled. In general, you should wait for all network requests to finish before calling success. For afterSave functions and afterDelete functions, which don't call success/error, Cloud Code will wait for all network requests to finish.

Here's an example where calling response.success will cancel an outstanding query, leading to unexpected results:

Parse.Cloud.define("ThisFunctionWillNotReturnAllDataAsExpected", function(request, response) {
  var results = "Retrieved all Posts:\n"
  var query = new Parse.Query("Post");

  // This query.find() is unlikely to finish before response.success() is called.
  query.find().then(function(posts) {
    for (var i = 0; i < posts.length; i++) {
      results += posts[i].get('title') + "\n";
    }  
  });

  response.success(results); // Response: "Retrieval all Posts:\n"
});

This is a common mistake that can be easily avoided. Make sure that you only call success once all of your network queries have returned data, such as in the following example:

Parse.Cloud.define("ThisFunctionWillReturnAllData", function(request, response) {
  var results = "Retrieved all Posts:\n"
  var query = new Parse.Query("Post");

  query.find().then(function(posts) {
    for (var i = 0; i < posts.length; i++) {
      results += posts[i].get('title') + "\n";
    }

    // success has been moved inside the callback for query.find()
    response.success(results);
  }, function(error) {
    // Make sure to catch any errors, otherwise you may see a "success/error not called" error in Cloud Code.
    response.error("Could not retrieve Posts, error " + error.code + ": " + error.message);
  });

});

HTTP requests have a maximum timeout of 1 minute. This timeout can be further restricted by the maximum timeout for the type of Cloud Code being executed as decribed in the timeouts section. For instance, HTTP requests in Cloud Functions are limited to 15 seconds, HTTP requests in before/after save triggers are limited to 3 seconds, etc.

Logging from Cloud Code

If you want to log a message to the log files displayed by parse log, you can use console.log, console.error, or console.warn. Both console.error and console.warn will write to the error log.

Parse.Cloud.define("Logger", function(request, response) {
  console.log(request.params);
  response.success();
});

Cloud functions may log up to 100 messages per request. Log lines are limited to 1KB in size, after which they are truncated.

Want to contribute to this doc? Edit this section.
Was this section helpful? NO YES Thanks for your feedback, we’re always working to make our docs better.

Advanced Cloud Code

Networking

Cloud Code allows sending HTTP requests to any HTTP Server using Parse.Cloud.httpRequest. This function takes an options object to configure the call. There is a limit of 8 concurrent httpRequests per Cloud Code request, and additional requests will be queued up.

A simple GET request would look like:

Parse.Cloud.httpRequest({
  url: 'http://www.parse.com/'
}).then(function(httpResponse) {
  // success
  console.log(httpResponse.text);
},function(httpResponse) {
  // error
  console.error('Request failed with response code ' + httpResponse.status);
});

Parse.Cloud.httpRequest returns a Promise that will be resolved on a successful http status code; otherwise the Promise will be rejected. In the above example, we use then() to handle both outcomes.

A GET request that specifies the port number would look like:

Parse.Cloud.httpRequest({
  url: 'http://www.parse.com:8080/'
}).then(function(httpResponse) {
  console.log(httpResponse.text);
}, function(httpResponse) {
  console.error('Request failed with response code ' + httpResponse.status);
});

Valid port numbers are 80, 443, and all numbers from 1025 through 65535.

Query Parameters

You can specify query parameters to append to the end of the url by setting params on the options object. You can either pass a JSON object of key value pairs like:

Parse.Cloud.httpRequest({
  url: 'http://www.google.com/search',
  params: {
    q : 'Sean Plott'
  }
}).then(function(httpResponse) {
  console.log(httpResponse.text);
}, function(httpResponse) {
  console.error('Request failed with response code ' + httpResponse.status);
});

or as a raw String like this:

Parse.Cloud.httpRequest({
  url: 'http://www.google.com/search',
  params: 'q=Sean Plott'
}).then(function(httpResponse) {
  console.log(httpResponse.text);
}, function(httpResponse) {
  console.error('Request failed with response code ' + httpResponse.status);
});

Setting Headers

You can send HTTP Headers by setting the header attribute of the options object. Let's say you want set the Content-Type of the request, you can do:

Parse.Cloud.httpRequest({
  url: 'http://www.example.com/',
  headers: {
    'Content-Type': 'application/json;charset=utf-8'
  }
}).then(function(httpResponse) {
  console.log(httpResponse.text);
}, function(httpResponse) {
  console.error('Request failed with response code ' + httpResponse.status);
});

Sending a POST Request

You can send a post request by setting the method attribute of the options object. The body of the POST can be set using the body. A simple example would be:

Parse.Cloud.httpRequest({
  method: 'POST',
  url: 'http://www.example.com/create_post',
  body: {
    title: 'Vote for Pedro',
    body: 'If you vote for Pedro, your wildest dreams will come true'
  }
}).then(function(httpResponse) {
  console.log(httpResponse.text);
}, function(httpResponse) {
  console.error('Request failed with response code ' + httpResponse.status);
});

This will send a post to http://www.example.com/create_post with body that is the url form encoded body attribute. If you want the body to be JSON encoded, you can instead do:

Parse.Cloud.httpRequest({
  method: 'POST',
  url: 'http://www.example.com/create_post',
  headers: {
    'Content-Type': 'application/json;charset=utf-8'
  },
  body: {
    title: 'Vote for Pedro',
    body: 'If you vote for Pedro, your wildest dreams will come true'
  }
}).then(function(httpResponse) {
  console.log(httpResponse.text);
}, function(httpResponse) {
  console.error('Request failed with response code ' + httpResponse.status);
});

To ensure that your HTTP request body is encoded correctly, please always include the charset in your Content-Type header.

Following Redirects

By default, Parse.Cloud.httpRequest does not follow redirects caused by HTTP 3xx response codes. You can use the followRedirects option to change this behavior to follow redirects:

Parse.Cloud.httpRequest({
  url: 'http://www.example.com/',
  followRedirects: true
}).then(function(httpResponse) {
  console.log(httpResponse.text);
}, function(httpResponse) {
  console.error('Request failed with response code ' + httpResponse.status);
});

The Response Object

The response object passed into the success and error will contain:

  • status - The HTTP Response status.
  • headers - The response headers
  • buffer - The raw byte representation of the response body.
  • text - The raw response body.
  • data - The parsed response, if Cloud Code knows how to parse the content-type that was sent.
  • cookies - The cookies sent by the server. They are Parse.Cloud.Cookie objects.

Modules

Cloud Code and Node.js are both built on the V8 JavaScript engine, but Cloud Code is not Node.js. npm packages are not guaranteed to work out of the box on Cloud Code.

Cloud Code supports breaking up JavaScript code into modules. You can check out this tutorial for an in depth look at creating your own. In order to avoid unwanted side effects from loading modules, Cloud Code's modules work similarly to CommonJS modules. When a module is loaded, the JavaScript file is loaded, the source executed and the global module.exports object is returned. For example, if cloud/name.js has the following source:

var coolNames = ['Ralph', 'Skippy', 'Chip', 'Ned', 'Scooter'];
module.exports.isACoolName = function(name) {
  return coolNames.indexOf(name) !== -1;
}

and cloud/main.js contains:

var name = require('cloud/name.js');
name.isACoolName('Fred'); // returns false
name.isACoolName('Skippy'); // returns true;
name.coolNames; // undefined.

name contains a function called isACoolName. The path used by require is relative to the root directory of your Parse project. Only modules in the cloud/ directory can be loaded.

Cloud Code Webhooks

Webhooks allow you to write your server-side logic in your own environment with any tools you wish to use. This can be useful if you want to use a language other than JavaScript, host it yourself for improved testing capabilities, or if you require a specialized library or technology not available in Cloud Code. Webhooks are currently available for beforeSave, afterSave, beforeDelete, afterDelete, and Cloud functions. To specify a new webhook, you can use the Parse Dashboard in the Webhooks section located under Core.

We've written an example Cloud Code Webhooks server, in Express.js, which you can find on Github: CloudCodeWebhooks-Express.

Note: At the current time, custom webhooks cannot be set for the special classes _User and _Installation.

Cloud Function Webhooks

A webhook request for a Cloud function will contain the following parameters:

  • master: True if the master key was used and false otherwise.
  • user: If set, this will contain the Parse User who made the request, in our REST API format. This is not set if the master key is used.
  • installationId: If available, the installationId which made the request.
  • params: A JSON object containing the parameters passed to the function. For example: { "foo": "bar" }
  • functionName: The name of the Cloud function.

To respond to this request, send a JSON object with the key error or success set. In the case of success, send back any data your client will expect; or simply true if your client doesn't require any data. In the case of error, the value provided should be the error message you want to return.

To create a webhook for a Cloud function, start by writing the function's code on your own server. Here's the simple hello world function written in a Rails environment.

# We need to disable CSRF protection for webhooks to work. Instead we
# use the webhook key to prove authenticity. protect_from_forgery :except => :index

def index
  # Ensure the request is authorized. You can find this key on your app's settings page
  # and you should ALWAYS validate it in your request.
  if request.headers['X-Parse-Webhook-Key'] !== @webhook_key
    return render :json => { :error => "Request Unauthorized"}
  end

  # Check the function name and return a message if it's correct
  if params[:functionName] == "helloWorld"
    return render :json => { :success => "Hello World!" }
  end

  # Return an error if it's not the function we expected
  return render :json => { :error => "Unknown function"}
end

Here's an example of the JSON data that would be sent in the request to this webhook:

// Sent to webhook
{
  "master": false,
  "user": {
    "createdAt": "2015-03-24T20:19:00.542Z",
    "objectId": "lValKpphWN",
    "sessionToken": "orU3ClA7sqMIN8g4KtmLe7eDM",
    "updatedAt": "2015-03-24T20:19:00.542Z",
    "username": "Matt"
  },
  "installationId": "b3ab24c6-2282-69fa-eeea-c1b36ea497c2",
  "params": {},
  "functionName": "helloWorld"
}

This response would indicate a success in the webhook:

// Returned from the webhook on success
{ "success": "Hello World!" }

This response would indicate an error in the webhook:

// Returned from the webhook on error
{ "error": "Error message >:(" }

You can activate this webhook from the Dashboard UI.

Once the webhook is set, you can call it from any of our SDKs or from the REST API, the same way you would a normal Cloud function.

Here's a more complex example where we use a webhook to perform some task for our billing pipeline. We'll use the popular resque gem to enqueue a job that handles billing the given user. For this example, the function is named chargeCustomer and it should always be called with the master key.

# We need to disable CSRF protection for webhooks to work. Instead we
# use the webhook key to prove authenticity.
protect_from_forgery :except => :index

def index
  # Ensure the request is validated
  if request.headers['X-Parse-Webhook-Key'] !== @webhook_key
    return render :json => { :error => "Request Unauthorized"}
  end

  # Check the function name
  if params[:functionName] == "chargeCustomer" && params[:master] == true
    # extract the custom parameters sent with the function
    custom_params = params[:params]
    user_id = custom_params["userObjectId"]

    # enqueue a resque job to bill the user
    Resque.enqueue(BillingJob, user_id)

    # return a json object of this billing info
    return render :json => { :success => "User billed!" }
  end

  return render :json => { :error => "Unknown function"}
end

Here's an example of the JSON data that would be sent in the request to this webhook:

// Sent to webhook
{
  "master": true,
  "installationId": "b3ab24c6-2282-69fa-eeea-c1b36ea497c2",
  "params": { "userObjectId": "6eaI2sTgH6" },
  "functionName": "chargeCustomer"
}

This response would indicate a success in the webhook:

// Returned from the webhook on success
{ "success": "User billed!" }

Set your webhook from the Dashboard UI. After that, it's available from all SDKs and the REST API the same way you would a normal Cloud function

Webhooks are great when you want to use a specialized technology not available on Parse's Cloud Code. In this case we made use of an open source library and integrated with a separate data source where our billing info might be stored for legacy reasons.

beforeSave Webhooks

Let's write a beforeSave trigger to truncate movie review comments that are more than 140 characters long using our own Rails server and a webhook.

For triggers, the following parameters are sent to your webhook.

  • master: True if the master key was used and false otherwise.
  • user: If set, this will contain the Parse User who made the request, in our REST API format.
  • installationId: If available, the installationId which made the request.
  • object: For triggers, this will contain the Parse Object, in our REST API format. For example: { "className": "TestObject", "foo": "bar" }.
  • triggerName: "beforeSave"

To respond to a beforeSave request, send a JSON object with the key error or success set. This is the same as for Cloud functions, but there's an extra capability with beforeSave triggers. By returning an error, you will cancel the save request and the object will not be stored on Parse. You can also return a JSON object in this following format to override the values that will be saved for the object:

{
  "className": "AwesomeClass",
  "existingColumn": "sneakyChange",
  "newColumn": "sneakyAddition"
}

Let's recreate our trigger to truncate movie review comments that are longer than 140 characters.

# We need to disable CSRF protection for webhooks to work. Instead we
# use the webhook key to prove authenticity.
protect_from_forgery :except => :reviews

def reviews
  if request.headers['X-Parse-Webhook-Key'] != @webhook_key
    return render :json => { :error => "Request Unauthorized"}
  end

  review = params[:object]
  if params[:triggerName] == "beforeSave" && review["className"] == "Review"
    # truncate the object and return the new data
    if review["comment"].length > 140
      review["comment"] = review["comment"].truncate(140)
      return render :json => { :success => review }
    end

    # if the comment is ok we just return a success
    return render :json => { :success => true }
  end

  return render :json => { :error => "Unknown trigger"}
end

Here's an example of the JSON data that would be sent in the request to this webhook:

// Sent to webhook
{
  "master": false,
  "user": {
    "createdAt": "2015-03-24T20:19:00.542Z",
    "objectId": "lValKpphWN",
    "sessionToken": "orU3ClA7sqMIN8g4KtmLe7eDM",
    "updatedAt": "2015-03-24T20:19:00.542Z",
    "username": "Matt"
  },
  "installationId": "b3ab24c6-2282-69fa-eeea-c1b36ea497c2",
  "triggerName": "beforeSave",
  "object": {
    "className": "Comment",
    "comment": "A very long comment that will be truncated to be just 140 characters. I sure love using Parse, it's just so easy to get started :)! Hopefully that part doesn't get truncated :/"
  }
}

This response would indicate a success in the webhook:

// Returned from the webhook on success
{
  "success": {
    "className": "Comment",
    "comment": "A very long comment that will be truncated to be just 140 characters. I sure love using Parse, it's just so easy to get started :)! Hopef..."
  }
}

afterSave Webhooks

Like we've seen in Cloud Code, it's also possible to run some code after an object has been saved using a webhook. The parameters sent to your webhook are the same as for beforeSave triggers but we'll repeat them here for clarity.

  • master: True if the master key was used and false otherwise.
  • user: If set, this will contain the Parse User who made the request, in our REST API format.
  • installationId: If available, the installationId which made the request.
  • object: For triggers, this will contain the Parse Object, in our REST API format. For example: { "className": "TestObject", "foo": "bar" }.
  • triggerName: "afterSave"

No response is required for afterSave triggers.

Let's take the same example we created in Cloud Code in the last chapter; keeping track of the number of comments on a blog post. But instead of storing the number in our Parse database, we'll store the count in a separate data source accessible by our Rails app. This could be useful if you're storing data that will be used to run custom analysics instead of being served to your users through a client.

# We need to disable CSRF protection for webhooks to work. Instead we
# use the webhook key to prove authenticity.
protect_from_forgery :except => :comments

def comments
  if request.headers['X-Parse-Webhook-Key'] != @webhook_key
    return render :nothing => true
  end

  comment = params[:object]
  if params[:triggerName] == "afterSave" && comment["className"] == "Comment"
    post = comment["post"]
    @post_model = Post.where("id = #{post["objectId"]}")
    @post_model.increment(:comments_count, 1)
    @post_model.save!
    return render :nothing => true
  end

  render :nothing => true
end

Here's an example of the JSON data that would be sent in the request to this webhook:

// Sent to webhook
{
  "master": false,
  "user": {
    "createdAt": "2015-03-24T20:19:00.542Z",
    "objectId": "lValKpphWN",
    "sessionToken": "orU3ClA7sqMIN8g4KtmLe7eDM",
    "updatedAt": "2015-03-24T20:19:00.542Z",
    "username": "Matt"
  },
  "installationId": "b3ab24c6-2282-69fa-eeea-c1b36ea497c2",
  "triggerName": "afterSave",
  "object": {
    "objectId": "zPnDyvj0vd",
    "className": "Comment",
    "createdAt": "2015-03-25T00:00:57.055Z",
    "updatedAt": "2015-03-25T00:00:57.055Z",
    "post": {
      "__type": "Pointer",
      "className": "Post",
      "objectId": "jsUd72Sd2l"
    }
  }
}

beforeDelete Webhooks

You also use webhooks for beforeDelete triggers. The parameters sent to your webhook are the same as for beforeSave and afterSave triggers but we'll repeat them here for clarity.

  • master: True if the master key was used and false otherwise.
  • user: If set, this will contain the Parse User who made the request, in our REST API format.
  • installationId: If available, the installationId which made the request.
  • object: For triggers, this will contain the Parse Object, in our REST API format. For example: { "className": "TestObject", "foo": "bar" }.
  • triggerName: "beforeDelete"

Just like for Cloud functions, to respond to a beforeDelete request, send a JSON object with the key error or success set. Returning an error will cancel the delete and the object will remain in your database.

As an example, let's use this trigger to prohibit a user from deleting or creating a new blog posts if they haven't paid their bill. We'll assume the billing information is currently stored in a SQL database only accessible from our Rails server. We'll use both the beforeDelete and the beforeSave triggers to disable all modifications to this class.

# We need to disable CSRF protection for webhooks to work. Instead we
# use the webhook key to prove authenticity.
protect_from_forgery :except => :posts

def posts
  if request.headers['X-Parse-Webhook-Key'] != @webhook_key
    return render :json => { :error => "Request Unauthorized"}
  end

  post = params[:object]
  if (params[:triggerName] == "beforeDelete" || params[:triggerName] == "beforeSave") && post["className"] == "Post"
    @user = User.find(post['user'])
    if !@user.paid_up
      return render :json => { :error => "You have outstanding charges on your account. Please update your credit card information before proceeding." }
    end

    return render :json => { :success => true }
  end
  return render :json => { :error => "Unknown trigger"}
end

Here's an example of the JSON data that would be sent in the request to this webhook:

// Sent to webhook
{
  "master": false,
  "user": {
    "createdAt": "2015-03-24T20:19:00.542Z",
    "objectId": "lValKpphWN",
    "sessionToken": "orU3ClA7sqMIN8g4KtmLe7eDM",
    "updatedAt": "2015-03-24T20:19:00.542Z",
    "username": "Matt"
  },
  "installationId": "b3ab24c6-2282-69fa-eeea-c1b36ea497c2",
  "triggerName": "beforeDelete",
  "object": {
    "objectId": "jsUd72Sd2l",
    "className": "Post",
    "createdAt": "2015-03-25T00:00:57.055Z",
    "updatedAt": "2015-03-25T00:00:57.055Z"
  }
}

This response would indicate a success in the webhook:

// Returned from the webhook on success
{ "success": true }

As with previous examples, for this example to work you would also need to set up the webhooks in the Dashboard for your app.

afterDelete Webhooks

The afterDelete trigger is also accessible via webhooks. The parameters sent to your webhook are the same as for other triggers but we'll repeat them here for clarity.

  • master: True if the master key was used and false otherwise.
  • user: If set, this will contain the Parse User who made the request, in our REST API format.
  • installationId: If available, the installationId which made the request.
  • object: For triggers, this will contain the Parse Object, in our REST API format. For example: { "className": "TestObject", "foo": "bar" }.
  • triggerName: "afterDelete"

No response is required for afterDelete triggers.

In our webhooks example for the afterSave trigger, we updated a count in our external SQL database to track the number of comments on a post. In this example, let's decrement this count when a comment is deleted.

# We need to disable CSRF protection for webhooks to work. Instead we
# use the webhook key to prove authenticity.
protect_from_forgery :except => :comments

def comments
  if request.headers['X-Parse-Webhook-Key'] != @webhook_key
    return render :nothing => true
  end

  comment = params[:object]
  if params[:triggerName] == "afterDelete" && comment["className"] == "Comment"
    @post_model = Post.where("id = #{comment['post']}")
    @post_model.decrement(:comments_count, 1)
    @post_model.save!
    return render :nothing => true
  end

  render :nothing => true
end

Here's an example of the JSON data that would be sent in the request to this webhook:

// Sent to webhook
{
  "master": false,
  "user": {
    "createdAt": "2015-03-24T20:19:00.542Z",
    "objectId": "lValKpphWN",
    "sessionToken": "orU3ClA7sqMIN8g4KtmLe7eDM",
    "updatedAt": "2015-03-24T20:19:00.542Z",
    "username": "Matt"
  },
  "installationId": "b3ab24c6-2282-69fa-eeea-c1b36ea497c2",
  "triggerName": "afterDelete",
  "object": {
    "objectId": "zPnDyvj0vd",
    "className": "Comment",
    "createdAt": "2015-03-25T00:00:57.055Z",
    "updatedAt": "2015-03-25T00:00:57.055Z",
    "post": {
      "__type": "Pointer",
      "className": "Post",
      "objectId": "jsUd72Sd2l"
    }
  }
}

After setting up your webhook in the Dashboard UI, you'll be acurately decrementing comment counts!

Resource Limits

  1. All webhooks are limited to 30 seconds. Parse will time out the request after this time limit.
  2. All webhooks have to handle the request as a POST request.
  3. Cloud Code Webhooks require an HTTPS connection. Your server must have a valid SSL certificate. Self-signed certificates will not be accepted, for your security.
  4. In cases where you define a webhook for a function or a trigger that you've also implemented in Cloud Code, Parse will only call your defined webhook. Priority will always be given to your webhook over Cloud Code.
  5. In order to secure these requests and prevent others from executing this code on your server, we'll send a secret key known only to you and Parse in the X-Parse-Webhook-Key header. You should always check this value against your WebhookKey to authenticate that the webhook request is coming from Parse. You can find your Webhook key in the Keys section of your app dashboard.

Background Jobs

Parse allows you to set up jobs that run in the background. Background Jobs are useful for long running tasks such as integrating with external sites where the response time could be slow, or sending out batched push notifications. If you commonly encounter timeout errors running Cloud functions then you should consider using a Background Job.

There are a few constraints that you need to keep in mind when using Background Jobs:

  • Jobs will be terminated after 15 minutes of run time.
  • Apps may have one job running concurrently per 20 req/s in their request limit.
  • Jobs that are initiated after the maximum concurrent limit has been reached will be terminated immediately.

Writing a Background Job

Writing a Background Job is similar to writing a Cloud function. Say you want to run a user migration job after adding a plan field to the Parse.User object. Your code would look like this:

Parse.Cloud.job("userMigration", function(request, status) {
  // Set up to modify user data
  Parse.Cloud.useMasterKey();
  var counter = 0;
  // Query for all users
  var query = new Parse.Query(Parse.User);
  query.each(function(user) {
      // Update to plan value passed in
      user.set("plan", request.params.plan);
      if (counter % 100 === 0) {
        // Set the  job's progress status
        status.message(counter + " users processed.");
      }
      counter += 1;
      return user.save();
  }).then(function() {
    // Set the job's success status
    status.success("Migration completed successfully.");
  }, function(error) {
    // Set the job's error status
    status.error("Uh oh, something went wrong.");
  });
});

As with other Cloud Functions, you should handle success and error conditions. For Background Jobs, you do this by calling either status.success() or status.error() when your function completes. Your job execution status will then be set to completed. If you don't call either of these methods, your job will time out in 15 minutes. You can optionally set a progress message while the job is executing by calling status.message(). If you call status.message() after status.success(), your progress message will be ignored.

Once you've deployed your code, you can test the job by running the following command, with your master key. Note that Background Jobs cannot be triggered from the client SDK. It is only available through the REST API.

curl -X POST \
  -H "X-Parse-Application-Id: $PARSE_APPLICATION_ID" \
  -H "X-Parse-Master-Key: $PARSE_MASTER_KEY" \
  -H "Content-Type: application/json;charset=utf-8" \
  -d '{"plan":"paid"}' \
  https://api.parse.com/1/jobs/userMigration

Setting up a Schedule

Once you've deployed your Background Job code, it can be scheduled in the Dashboard under the Cloud Code tab. The Scheduled Jobs pane lists all currently scheduled jobs and allows you to schedule a new one. To add an entry to the job schedule, select a currently deployed job then specify a description, any required parameters, the start time, and the frequency. Once a job has been scheduled you can run it on demand by clicking Run Now. You may also delete an entry in the job schedule. The Job Status pane lists the results of your job executions. You can see when a job started, its most recent status message, and whether it has completed.

Want to contribute to this doc? Edit this section.
Was this section helpful? NO YES Thanks for your feedback, we’re always working to make our docs better.

Cloud Code Hosting

Parse Hosting provides you with the tools to host static and dynamic websites. You can upload arbitrary static web content or create dynamic web apps, using the JavaScript SDK on the client side and Cloud Code plus Express on the server side. This allows you to create companion web apps for your native app, landing pages for your mobile app, or even host Unity Web Player binaries.

On the computer you use for development, you will need to install Parse's command line tool to manage your website in the Parse Cloud. Take a look at the Cloud Code guide or the Command Line Tool guide for help getting started.

You will need at least version 1.1.0 of the command line tool.

A Simple Website

Hosting static content using Parse is easy. Everything in the public directory will be hosted at your-custom-subdomain.parseapp.com. This directory sits alongside the cloud and config directories.

To deploy a Hello World website, simply do:

$ echo "Hello World" > public/index.html
$ parse deploy

To access the website you've deployed you need to set up a subdomain.

Choosing a Subdomain Name

To access your hosted content, you will first need to select a ParseApp subdomain. You can set your subdomain in the "Web Hosting" section of your app's settings. There will be a field for you to enter a ParseApp name. The subdomain name is case-insensitive, and unlike your app name, it must be unique across the entire Parse system. It may consist of 3 to 20 alphanumeric characters and dashes, and may not start or end with a dash. When you select a unique subdomain name, a checkmark is displayed to indicate that is is not yet taken.

Let's say you chose at-the-movies as the subdomain name for your website, you can now access it at the root URL at-the-movies.parseapp.com. If you upload another hosted file from public/images/background.png, then it can be accessed by pointing your browser at at-the-movies.parseapp.com/images/background.png.

Uploading Constraints

There are a few constraints for hosted files:

  • Files are limited to 500 megabytes.
  • You cannot upload more than 500 hosted files.
  • Filenames should begin with an alphanumeric character and consist only of alphanumeric characters, dashes, underscores, spaces, and '@' signs.
  • The command line tool will automatically skip emacs and vim autosave files.

You are free to upload hosted content of any type you want. Parse will infer the content type from each file's extension.

Custom Domain Names

Users can also host content at a custom domain name. If you have chosen the ParseApp name at-the-movies and want to host your content at www.example.com, it requires three steps:

  1. Demonstrate that you control the DNS entries for www.example.com. You can do this by adding a CNAME to at-the-movies.parseapp.com from either www.example.com or [your_host_name_key].www.example.com. Your host name key is a 12-character string that you can find in the "Web Hosting" section in your app's settings. It can take anywhere from 15 minutes to several hours for a new DNS entry to be propagated, and the next step cannot be completed until this happens. If you are currently hosting a website at www.example.com, you will probably want to use the host name key method, otherwise your website might experience downtime for users who see the new DNS entry before you complete the next step.
  2. After your new DNS entry been propagated, you can set your host name to www.example.com in your app's hosting settings. If the first step was successful, a checkmark should show up. Otherwise, an error message will tell you what went wrong.
  3. If you verified ownership via the host name key, you will still need to add a CNAME from www.example.com to at-the-movies.parseapp.com to begin sending traffic to your hosted app.

After completing these steps, www.example.com will serve the same content as at-the-movies.parseapp.com.

Apex Domains

If you want to serve content at an apex domain like example.com then you might have trouble with the steps above, because root domains generally don't support CNAME records. To support this situation, Parse offers a service which will redirect traffic from root domains like example.com to the www.example.com subdomain. Note that redirecting traffic from www.example.com to example.com is not supported.

To use this service, complete the steps above using a www-prefixed domain like www.example.com, and then create A records from example.com to one or both of the following IP addresses:

  • 54.85.233.145
  • 54.85.226.190

A records can be created in the settings page of your domain name service provider. After creating these A records, requests to example.com will result in a 301 (permanent) redirect to www.example.com while preserving the request path.

HTTPS

If you visit your hosted site over HTTPS using your custom domain, you might see a warning that the website is serving content using an SSL certificate belonging to *.parseapp.com. If you have a SSL certificate belonging to your custom domain, you can fix these warnings by uploading the public certificate file and the private key file on the "Web Hosting" section in your app's settings. If your domain's public certificate requires intermediate certificates, then you should append your public certificate, intermediate certificates, and the root certificate into a single file (in that order), and upload it as the "SSL Public Certificate" in your app settings. In 10-30 minutes, your certificate will be propagated to the Parse hosting servers and served to users who visit your hosted site over HTTPS.

Dynamic Websites

The version of Express used by Parse Cloud Code, Express 3, is no longer maintained. If you're building a new dynamic website on Parse, we recommend using Webhooks to connect your Parse app to a Node server hosted on Heroku.

You can use Cloud Code and Express to build multi-page dynamic web apps. With the Express framework, you have many powerful tools at your fingertips, such as request routing, cookie handling, and template rendering. With Cloud Code, you have access to functionality such as interacting with Parse data and sending HTTP requests.

Express can help you get your app up and running quickly, but if you prefer a lower-level Node.js-like HTTP interface, we have that too. For more details, please see our HTTP interface API docs. If you choose to use Express or Node.js, you'll first need to delete public/index.html so that requests can get through to your custom handler functions.

In this guide, we'll focus on building web apps with the Express API.

Creating a Web App

You can specify a template engine by using the --type flag. For example, you can use parse generate --type="express-jade" to use jade instead of ejs.

After you get Parse Hosting set up, you can generate a starter web app by typing the following inside your parse project folder.

$ parse generate

This command creates the following directory structure inside your cloud folder. It will not touch your existing main.js file.

-cloud/
  app.js
  -views/
    hello.ejs
  main.js (not touched)

Next, you need to add the following line at the top of your main.js. This makes sure that the code in app.js is loaded.

require('cloud/app.js');

Then, run parse deploy to deploy your web app. After deploying, you should find your web app at your-custom-subdomain.parseapp.com/hello. We'll next go over this sample app in detail.

Sample Web App

Let's go over the starter web app to get an idea what Express can do for us. If you haven't created the starter app yet, you can create one using these instructions.

The top-level entry point for an Express app is app.js, where the app is initialized, and the request paths are hooked up to corresponding logic through the Express routing API. You must require this file from main.js because Cloud Code starts at main.js when it loads your JavaScript. We recommend that you put your Cloud Functions in main.js, and put all Express-related code in app.js.

In your sample app, app.js should look like this:

// These two lines are required to initialize Express.
var express = require('express');
var app = express();

// Global app configuration section
app.set('views', 'cloud/views');  // Specify the folder to find templates
app.set('view engine', 'ejs');    // Set the template engine
app.use(express.bodyParser());    // Middleware for reading request body

// This is an example of hooking up a request handler with a specific request
// path and HTTP verb using the Express routing API.
app.get('/hello', function(req, res) {
  res.render('hello', { message: 'Congrats, you just set up your app!' });
});

// This line is required to make Express respond to http requests.
app.listen();

In the global app configuration section at the top, we specify some app settings and initialize the Express middleware. App settings include specifying a templating engine for rendering your web pages, and where to find your template source files. Express middleware are optional components that preprocess the incoming request. The middleware specified in this section apply to all request paths.

The sample app also has a cloud/views folder, containing an EJS template file (hello.ejs). In this template, the message variable will be replaced by the value specified in the res.render() line in app.js.

Handling Requests

Let's look at a simple request handler that reads the request text, and responds with a message including the request text.

app.post('/echo', function(req, res) {
  res.set('Content-Type', 'text/plain');
  res.send('echoing: ' + req.body.message);
});

Every request handler starts with app.VERB, where the VERB could be any of the standard HTTP verbs, such as get, post, put, or delete. This tells Express what type of HTTP request this handler should respond to.

Next, the '/echo' parameter specifies what url path (also known as route) that this request handler should respond to. Then, we specify a function that takes in a request and response object to perform the request handling logic. As long as we include the express.bodyParser middleware, the req.body should be populated with input data. For example, if the raw request body is { "message": "hi" }, then req.body.message above will have the value 'hi'. Finally, the res.send() tells Express to populate the response object with the specified string.

Express Middleware

Middleware are modules that process the request before it reaches your request handler. These components convert a raw HTTP request into a request object that you can easily work with in your request handler. Cloud Code supports the following Express-provided middleware:

  • express.basicAuth() - Allows you to set up HTTP basic authentication for your website.
  • express.bodyParser() - Converts the HTTP request body (JSON or www-form-encoded) into the request.body object.
  • express.methodOverride() - A standard web form only supports HTTP post. This middleware lets the web form issue put/delete HTTP requests to Express. This is especially useful when you want to closely follow REST-ful principles.
  • express.cookieParser() - Reads the request cookie sent by the browser, and puts that into the request.cookies and express.request.signedCookies objects.
  • express.cookieSession() - Allows you to store session data in a signed cookie by setting the request.session object
  • express.csrf() - Protects against cross-site request forgery.

You can add middleware to your app with app.use(). The standard Express middleware are functions, so make sure you call them accordingly (e.g. app.use(express.csrf()). You should add your app's middleware before registering any request handlers with app.VERB().

Please follow the above order when configuring Express middleware in the global app configuration section. You may leave out any middleware that you don't need. The order is important because later middleware may depend on the data created by earlier ones.

Custom Middleware

In addition to the Express-provided middleware listed above, we've provided the following custom middleware:

parseExpressCookieSession

parseExpressCookieSession is an Express middleware for Parse.User session management through a signed browser cookie. To use this middleware in Cloud Code, you need to:

  1. Call require('parse-express-cookie-session') from your JavaScript file, and configure your app to use this middleware after express.cookieParser. You must also specify a cookie signing secret for express.cookieParser.
  2. Write an Express endpoint for logging in. In this endpoint, you should call Parse.User.logIn() with the user-specified username and password. This sets a cookie in the user's browser.
  3. On subsequent requests from the same user's browser, Parse will automatically set the current user in Cloud Code based on this cookie. You can access this user object through Parse.User.current().
  4. Finally, you should also make an Express endpoint for logging out. In this endpoint, you should call Parse.User.logOut() to remove the browser cookie.

This middleware only works when your app is accessed through an HTTPS connection. You can use the parseExpressHttpsRedirect() middleware to force all requests to your website to be HTTPS.

By default, we do not fetch the current user object. This means although Parse.User.current() will be defined, you will not be able to retrieve user object fields until you call Parse.User.current().fetch(). Fetching the user is not always necessary. When you query for other objects, Parse.ACL will work correctly even without fetching the user object. You can override this option and always fetch the user, but please be aware that always fetching the user will result in one extra Parse API request during each Express request. Therefore we recommend that you leave this option as false, and only fetch the current user when you need to.

Here is some example code from an app that has Note objects with ACLs:

  var express = require('express');
  var parseExpressHttpsRedirect = require('parse-express-https-redirect');
  var parseExpressCookieSession = require('parse-express-cookie-session');
  var app = express();

  app.set('views', 'cloud/views');
  app.set('view engine', 'ejs');
  app.use(parseExpressHttpsRedirect());  // Require user to be on HTTPS.
  app.use(express.bodyParser());
  app.use(express.cookieParser('YOUR_SIGNING_SECRET'));
  app.use(parseExpressCookieSession({ cookie: { maxAge: 3600000 } }));

  // You could have a "Log In" link on your website pointing to this.
  app.get('/login', function(req, res) {
    // Renders the login form asking for username and password.
    res.render('login.ejs');
  });

  // Clicking submit on the login form triggers this.
  app.post('/login', function(req, res) {
    Parse.User.logIn(req.body.username, req.body.password).then(function() {
      // Login succeeded, redirect to homepage.
      // parseExpressCookieSession will automatically set cookie.
      res.redirect('/');
    },
    function(error) {
      // Login failed, redirect back to login form.
      res.redirect('/login');
    });
  });

  // You could have a "Log Out" link on your website pointing to this.
  app.get('/logout', function(req, res) {
    Parse.User.logOut();
    res.redirect('/');
  });

  // The homepage renders differently depending on whether user is logged in.
  app.get('/', function(req, res) {
    if (Parse.User.current()) {
      // No need to fetch the current user for querying Note objects.
      var Note = Parse.Object.extend("Note");
      var query = new Parse.Query(Note);
      query.find().then(function(results) {
        // Render the notes that the current user is allowed to see.
      },
      function(error) {
        // Render error page.
      });
    } else {
      // Render a public welcome page, with a link to the '/login' endpoint.
    }
  });

  // You could have a "Profile" link on your website pointing to this.
  app.get('/profile', function(req, res) {
    // Display the user profile if user is logged in.
    if (Parse.User.current()) {
      // We need to fetch because we need to show fields on the user object.
      Parse.User.current().fetch().then(function(user) {
        // Render the user profile information (e.g. email, phone, etc).
      },
      function(error) {
        // Render error page.
      });
    } else {
      // User not logged in, redirect to login form.
      res.redirect('/login');
    }
  });

  app.listen();

The corresponding login.ejs could look like:

  <html>
    <head></head>
    <body>
      <form method="post" action="/login">
        <label>Username</label>
        <input name="username"></input>
        <label>Password</label>
        <input name="password" type="password"></input>
        <input class="button" type="submit" value="Log In">
      </form>
    </body>
  </html>
parseExpressHttpsRedirect

parseExpressHttpsRedirect is an Express middleware for redirecting the user to the HTTPS endpoint with the same URL. To use this middleware, you must require it from your JavaScript file:

var parseExpressHttpsRedirect = require('parse-express-https-redirect');

Then you can use the middleware to redirect all requests to HTTPS.

app.use(parseExpressHttpsRedirect());

You should place this middleware as early as possible in the global app configuration section, so that HTTP requests will do less work (and run faster) before they are redirected to the HTTPS URL.

parseExpressRawBody

parseExpressRawBody is an Express middleware for parsing the request body into a byte Buffer. To use this middleware, you must require it from your JavaScript file:

var parseExpressRawBody = require('parse-express-raw-body');

This can be used in your Express app to parse the request body into a Buffer object at req.body.

You can use this as a fallback if express.bodyParser middleware cannot parse the incoming request body. If your app needs to use both express.BodyParser and this, then you must place this middleware after express.bodyParser in the app configuration section.

If you need the string representation of the request body, you can access it at req.body.toString().

  var express = require('express');
  var parseExpressRawBody = require('parse-express-raw-body');
  var app = express();

  app.use(express.bodyParser());
  app.use(parseExpressRawBody());

  app.post('/receive_data', function(req, res) {
    // If the request body is JSON or form-urlencoded, then the express.bodyParser
    // middleware will populate req.body with the parsed contents.  Otherwise,
    // parseExpressRawBody middleware will fill req.body with a Buffer object
    // containing bytes in the request body.
    res.send('OK');
  });

  app.listen();

Rendering Templates

Templates are a great way to dynamically generate web content, and reduce code duplication. Cloud Code provides modules for the EJS and Jade template engines.

You specify the template engine in the global app configuration section with app.set('view engine', ENGINE_NAME), where ENGINE_NAME can be either 'ejs' or 'jade'. The template file in the cloud/views folder should have an extension matching ENGINE_NAME. The sample app's template looks like this in EJS (hello.ejs) or Jade (hello.jade):

// Using EJS syntax
<!DOCTYPE html>
<html>
  <head>
    <title>Sample App</title>
  </head>
  <body>

# Hello World

<%%= message %>

  </body>
</html>
// Using Jade syntax
doctype 5
html
  head
    title Sample App
  body
    h1 Hello World
    p= message

You can find more information about the syntax for each template engine at the homepages for EJS and Jade.

You render templates by calling res.render(). The first argument is the template file name. If it does not have a file extension, Express will look for the file with the extension matching your app's template engine setting. The second argument is a JSON object that contains all the variables in the template and their corresponding values. Each variable in the template serves as a placeholder, and is replaced by its actual value when you render the template.

Getting User Input

Getting user input is easy with Express in Cloud Code. You can create a form element in a template to allow the user to type some text, and then add a request handler for the request issued by submitting the form.

Let's add a simple form to our sample app template. Please replace hello.ejs or hello.jade with the following code:

// Using EJS syntax
<!DOCTYPE html>
<html>
  <head>
    <title>Sample App</title>
  </head>
  <body>

# Hello World

    <p><%%= message %>

    <form method="post" action="/hello">

        <input name="message"></input>
        <input class="button" type="submit" value="Update Greeting">

    </form>
  </body>
</html>
// Using Jade syntax
doctype 5
html
  head
    title Sample App
  body
    h1 Hello World
    p= message
    form(method="post", action="/hello")
      p
        input(type="text", name="message")
        input(type="submit", name="submit", value="Update Greeting")

Then, we need to add a request handler for the HTTP post request in app.js.

app.post('/hello', function(req, res) {
  res.render('hello', { message: req.body.message });
});

Suppose the user types "hi" into the text box, and then clicks the "Update Greeting" button. The form will send an HTTP post request to the url http://example.parseapp.com/hello, with the request body message=hi. The express.bodyParser middleware will read the request body and set req.body.message to 'hi'. The request then triggers the above request handler because the HTTP verb and url path both match. Finally, the request handler renders the hello.ejs template by inserting 'hi' into the placeholder for the message variable.

User Session Management

You can add Parse.User authentication and session management to your Express app using the parseExpressCookieSession middleware. You just need to call Parse.User.logIn() in Cloud Code, and this middleware will automatically manage the user session for you.

You can use a web form to ask for the user's login credentials, and log in the user in Cloud Code when you receive data from this form. After you call Parse.User.logIn(), this middleware will automatically set a cookie in the user's browser. During subsequent HTTP requests from the same browser, this middleware will use this cookie to automatically set the current user in Cloud Code. This will make ACLs work properly in Cloud Code, and allow you to retrieve the entire current user object if needed. Finally, when you log out a user in Cloud Code by calling Parse.User.logOut(), this middleware will automatically remove the browser cookie. For sample app code, please see the documentation for this middleware.

When you work with user data, you should use HTTPS whenever possible. To protect your app and your users, the parseExpressCookieSession middleware requires you to use HTTPS. For your convenience, we also provide a parseExpressHttpsRedirect middleware for redirecting all HTTP requests to HTTPS. Please see its documentation for details.

Static Content

Your Express app can sit side-by-side with any static content you deployed from your public folder. When a request goes to a URL of your subdomain, Parse will first look for a matching file in the public directory. If there is no match, then Parse will invoke any Express request handlers that you have registered in Cloud Code. If there is still no match, Parse will render a "404 Not Found" page.

If you are using Express with static content, we recommend the following directory structure for your project.

-cloud/
  main.js            Cloud Code functions, require cloud/app.js here
  app.js             Express app configuration and request handling logic
  -views/            View template files that Express needs to render
    hello.ejs
-public/
  example.html       Static HTML files
  favicon.ico        Your favicon logo
  -stylesheets/      CSS stylesheets
    style.css

Logging

If you want to log a message to the log files displayed by parse log, you can use console.log, console.error, or console.warn. Both console.error and console.warn will write to the error log.

app.post('/hello', function(req, res) {
  console.log("New message: " + req.body.message);
  res.render('hello', { message: req.body.message });
});

Development vs Production

You can use your development app to try out new code, and the production app to run your app that you distribute to the public.

Adding a New App to a Project

You will need to have multiple apps linked to your project. parse new will link the first app to the project. You can add more apps by running parse add [alias], like so:

$ parse add production
Email: pirate@gmail.com
Password:
1:PiecesOfEightCounterProd
2:PiecesOfEightCounterDev
Select an App: 1

The example above links the PiecesOfEightCounterProd app to your project. It also creates an alias to new app called production that provides a shorthand way to reference the app.

Developing your Website

While developing new code, you can use the develop command to have the Parse command line tool continuously check for updates to your project and upload your changes. The command looks like:

$ parse develop development
E2013-11-21T01:05:56.257Z] Deploy failed with error:Error: Uncaught SyntaxError: Unexpected token ; in app.js:30
    at main.js:1:1
I2013-11-21T01:06:21.504Z] Deployed v172 with triggers:
  Cloud Functions:
    hello

Note that for the develop command you need to be explicit about the app that you are going to push new changes to. This avoids accidentally running develop on your production app, potentially deploying untested code to it. The command line tool will upload code changes and display new log messages, until you hit Ctrl-C.

Deploying Code to Production

After you are done testing and updating your code, you can deploy the code to production by passing the production app to the the deploy command, like so:

$ parse deploy production
New release is named v2
Want to contribute to this doc? Edit this section.
Was this section helpful? NO YES Thanks for your feedback, we’re always working to make our docs better.

Command Line Tool

The Parse command line tool allows you to interact with your Cloud Code from the terminal. Some of the topics here are also covered in the Cloud Code guide, but they are repeated here for clarity.

Installation

Mac and Linux

In Mac OS X and Linux/Unix environments, you can get the parse command line tool by running this command:

curl -s https://www.parse.com/downloads/cloud_code/installer.sh | sudo /bin/bash

This installs a tool named "parse" to /usr/local/bin/parse. There's no other junk, so to uninstall, just delete that file. This will also update your command line tool if you already have it installed.

Windows

The Parse command line tool for Windows is available here. Download the Windows executable named: parse.exe at this link. Note that this is not an installer, it is just a plain Windows executable.

Creating a Parse app

You can create a new Parse app using parse new. It asks you a series of questions and at the end of it, you will have a new Parse app (with the given name). Additionally, you will also set up a Cloud Code project for the app. You can test that everything works by performing parse deploy and executing the curl command printed at the end of output.

$ parse new
Would you like to create a new app, or add Cloud Code to an existing app?
Type "(n)ew" or "(e)xisting": n
Please choose a name for your Parse app.
Note that this name will appear on the Parse website,
but it does not have to be the same as your mobile app's public name.
Name: MyParseApp
Which of these providers would you like use for running your server code:
1) Heroku (https://www.heroku.com)
2) Parse  (https://parse.com/docs/cloudcode/guide)
Type 1 or 2 to make a selection: 2

Awesome! Now it's time to set up some Cloud Code for the app: "MyParseApp",
Next we will create a directory to hold your Cloud Code.
Please enter the name to use for this directory,
or hit ENTER to use "MyParseApp" as the directory name.

Directory Name: my_parse_app

You can either set up a blank project or create a sample Cloud Code project.
Please type "(b)lank" if you wish to setup a blank project, otherwise press ENTER:
Successfully configured email for current project to: "${ACCOUNT_EMAIL}"
Your Cloud Code has been created at ${CUR_DIR}/my_parse_app.

This includes a "Hello world" cloud function, so once you deploy,
you can test that it works, with the printed curl command.

Next, you might want to deploy this code with:

    parse deploy

Once deployed you can test that it works by running:
curl -X POST \
 -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
 -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
 -H "Content-Type: application/json" \
 -d '{}' \
 https://api.parse.com/1/functions/hello

Setting up Cloud Code

If you already created a Parse app (for example, through the website or apps API) and want to set up Cloud Code for the app, parse new can still help you.

$ parse new
Would you like to create a new app, or add Cloud Code to an existing app?
Type "(n)ew" or "(e)xisting": e
1:    MyApp
2:    MyOtherApp
Select an App to add to config: 1
Which of these providers would you like use for running your server code:
1) Heroku (https://www.heroku.com)
2) Parse  (https://parse.com/docs/cloudcode/guide)
Type 1 or 2 to make a selection: 2

Please enter the name of the folder where we can download the latest deployed
Cloud Code for your app "MyApp"

Directory Name: myapp

You can either set up a blank project or download the current deployed Cloud Code.
Please type "(b)lank" if you wish to setup a blank project, otherwise press ENTER:
Successfully configured email for current project to: "${ACCOUNT_EMAIL}"
Your Cloud Code has been created at ${CUR_DIR}/myapp.

This includes a "Hello world" cloud function, so once you deploy,
you can test that it works, with the printed curl command.

Next, you might want to deploy this code with:

    parse deploy

Once deployed you can test that it works by running:
curl -X POST \
 -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
 -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
 -H "Content-Type: application/json" \
 -d "{}" \
 https://api.parse.com/1/functions/hello

Note: This flow assumes that you already set up account keys. See also multiple account keys, if you are developing on apps belonging to different Parse accounts.

The myapp directory looks like:

myapp
β”œβ”€β”€ cloud
β”‚   └── main.js
β”œβ”€β”€ .parse.local
β”œβ”€β”€ .parse.project
└── public
    └── index.html

.parse.local and .parse.project are config files that store application info, and project level info (for instance, Javascript SDK version), respectively. The cloud directory stores your Cloud Code, and the public directory stores any static content that you want to host on Parse. In the cloud directory, you'll typically just be editing main.js, which stores all of your Cloud Code functions.

For now, just check that these files were created successfully. If you're using source control, you can check all of these files in.

We recommend using source control to check in all of these files. If you're not already set up with source control, try this tutorial from GitHub.

The same code can be deployed to multiple different applications. This is useful so that you can have separate development and production applications. Then you test the code on a development application before launching it in production.

The first application that is added (by the new command) will be the default application for all command line operations. All commands except new take an optional app name that the command will be performed on.

Deploying

To deploy a new release, run parse deploy from the command line:

$ parse deploy
Uploading source files
Uploading recent changes to scripts...
The following files will be uploaded:
...
Uploading recent changes to hosting...
The following files will be uploaded:
...
Finished uploading files
New release is named vx (using Parse JavaScript SDK vx.x.x)

This pushes the new code (in cloud and public) to the Parse Cloud and deploys this code for the default target which is the first app that was added or the one you set using parse default. You can choose to deploy to a different target by adding the target as an argument to deploy:

$ parse deploy "My Other App"
Uploading source files
Uploading recent changes to scripts...
The following files will be uploaded:
.....
Uploading recent changes to hosting...
The following files will be uploaded:
.....
Finished uploading files
New release is named vx (using Parse JavaScript SDK vx.x.x)

You can add release notes to the deploy with the -d or --description option

If the contents of your parse project remain unchanged then we skip deploy. You will see an output like:

$ parse deploy
Uploading source files
Finished uploading files
Not creating a release because no files have changed

You can override this behavior with the -f or --force flag. Providing this flag forces a deploy despite no changes to your project.

When embedding parse deploy within other scripts (such as in an automated testing/deploy environment) you can rely on the exit code from the Parse command line tool to indicate whether the command succeded. It will have an exit code of 0 on success and a non-zero exit code when the deploy failed.

A Simple Function

Following ancient tradition, let's see how to run the simplest possible function in the cloud. If you take a look at cloud/main.js, you'll see an example function that just returns a string:

Parse.Cloud.define("hello", function(request, response) {
  response.success("Hello world!");
});

To deploy the code from your machine to the Parse Cloud, run this from the command line:

$ parse deploy

Once deployed, this function can be called from any of the SDKs or the command line like so:

// Android SDK
ParseCloud.callFunctionInBackground("hello", new HashMap<String, Object>(), new FunctionCallback<String>() {
  void done(String result, ParseException e) {
    if (e == null) {
      // result is "Hello world!"
    }
  }
});
// iOS, OS X, tvOS, watchOS SDK: Objective-C
[PFCloud callFunctionInBackground:@"hello"
                   withParameters:@{}
                            block:^(NSString *result, NSError *error) {
   if (!error) {
     // result is @"Hello world!"
   }
}];
// iOS, OS X, tvOS, watchOS SDK: Swift
PFCloud.callFunctionInBackground("hello", withParameters: nil) {
  (response: AnyObject?, error: NSError?) -> Void in
  let responseString = response as? String
}
// PHP SDK
$result = ParseCloud::run("hello", []);
// .NET SDK
ParseCloud.CallFunctionAsync<IDictionary<string, object>>("hello", new Dictionary<string, object>()).ContinueWith(t => {
  var result = t.Result;
// result is "Hello world!"
});
// JavaScript SDK
Parse.Cloud.run('hello', {}).then(function(result) {
  // result is 'Hello world!'
}, function(error) {
    // handle error
});
# REST API, command line
curl -X POST \
 -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
 -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
 -H "Content-Type: application/json" \
 -d '{}' \
 https://api.parse.com/1/functions/hello

You should see this response:

{ "result": "Hello world!" }

Congratulations! You've successfully deployed and run Cloud Code.

This is a good time to play around with the deployment cycle. Try changing "Hello world!" to a different string, then deploy and run the function again to get a different result. The whole JavaScript SDK is available in Cloud Code, so there's a lot you can do. The Cloud Code guide goes over various examples in more detail.

Developing Cloud Code

You can also run the Parse command line tool in development mode using the develop command. This will make the tool watch the source directory for any updates and deploy them to Parse. It also provides a live stream of Cloud Code logs.

$ parse develop hello
Your changes are now live.
I2015-08-15T17:01:02.214Z]Deployed v2 with triggers:
  Cloud Functions:
    hello

I2015-08-06T01:30:19.366Z]Deployed v1 with triggers:
  Cloud Functions:
    hello

I2015-08-06T01:30:25.110Z]v1 Ran cloud function hello with:
  Input: {}
  Result: Hello world!

Unlike the other commands, for develop you must specify the Parse App to push updates to. This is to avoid accidentally running develop on your production app causing you to run untested code in your production app.

Adding a New Target

You can add a new Parse app as a target by running the add command.

$ parse add
1: MyApp
2: MyOtherApp
Select an App to add to config:

The add command takes an optional argument which is an alias to assign to the application that can be used instead of the app name.

Setting the Default App

parse deploy, parse logs, parse rollback, and parse releases use the default app to be run against the commands. parse default allows you to change this default app.

$ parse default MyApp
Default app set to MyApp.
$ parse default
Current default app is MyApp.

Rolling Back

You can roll back a release using parse rollback. Just like with parse deploy, you can specify an optional target argument.

$ parse rollback
Rolled back to v1

This rolls back to the previous version of the code. You can also specify the release name to roll back to by using the -r or --release option.

Reading the Logs

Every deploy, rollback, and activation of Cloud Code is logged. You can retrieve the end of logs using the parse logs command. There are two types of logs:

  • INFO - contains everything.
  • ERROR - contains only the errors.

The logs command takes an optional target as well as two options:

  • -f, --follow=false: Emulates tail -f and streams new messages from the server
  • -l, --level="INFO": The log level to restrict to. Can be 'INFO' or 'ERROR'.
  • -n, --num=10: The number of the messages to display
$ parse logs -n 1
I2015-08-15T17:01:02.214Z] beforeSave handler in release 'v1' ran for GameScore with the input:
  {"original": null, "update":{"score": 1337}}
 and failed validation with Each GamesScore must have a playerName

Listing Releases

You can list the known set of releases on the Parse Cloud with the releases command. Parse only tracks the last 10 releases.

$ parse releases
Name                            Description                     Date
v14                             Add background job              2015-08-11T18:12:52Z
v15                             No release notes given          2015-08-22T18:45:30Z
v16                             Moved to webhooks               2015-08-26T21:30:06Z
...

To view all files uploaded in a given release you can use the -v or --version option.

$ parse releases -v v14
Deployed cloud code files:
main.js

Deployed public hosting files:
index.html

Setting the Javascript SDK version

The default Parse JavaScript SDK version that is used for the Cloud Code in this directory is the latest version at the time the new command was run for this directory. If you want to change this, use parse jssdk. You can see all available Parse JavaScript SDKs using parse jssdk -a. You can also use parse jssdk to check which Parse JavaScript SDK version is currently being used.

$ parse jssdk
Current JavaScript SDK version is 1.5.0
$ parse jssdk -a
*  1.5.0
   1.4.2
   1.4.0
   1.3.5
   1.3.4
   1.3.3
   1.3.2
$ parse jssdk 1.4.2
Current JavaScript SDK version is 1.4.2
$ parse jssdk -a
   1.5.0
*  1.4.2
   1.4.0
   1.3.5
   1.3.4
   1.3.3
   1.3.2

Updating

You can update the command line tool using parse update. It will update your Parse command line tool to the latest version.

For instance, if you run this command on Mac OS X:

Downloading binary from https://github.com/ParsePlatform/parse-cli/releases/download/release_${LATEST_VERSION}/parse.
Successfully updated binary at: /usr/local/bin/parse

Secure Config Format

If you are using a version of command line tool older than 2.2.2 the Cloud Code project you create has the following structure:

.
β”œβ”€β”€ cloud
β”‚   └── main.js
β”œβ”€β”€ config
β”‚   └── global.json
└── public
    └── index.html

All project configuration like: applications to deploy to and their master keys, default application associated with the project and Javascript SDK version to use are stored in the config file at config/global.json.

If you did not want to expose your master keys but still wanted to share your code with someone, and want them to use the same project level configurations (for instance, Javascript SDK version) that was not possible.

We have now broken up the config into two parts:

  • .parse.project: project level config
  • .parse.local: user level config

This is the new project structure:

β”œβ”€β”€ cloud
β”‚   └── main.js
β”œβ”€β”€ .parse.local
β”œβ”€β”€ .parse.project
└── public
    └── index.html

Storing master keys along with application code is in general not a good idea. Master keys can be used to perform various irreversible operations on your application, and it is impossible to track down the offender.

Whether you are using an older version of config or the latest preferred version, going forward the command line tool (version > 2.2.2) will not store master key when you perform parse add. Further, parse new will always create a project with the latest preferred config format.

If you wish to migrate to the latest preferred, more secure config format you can use the parse migrate command

$ parse migrate
Successfully migrated to the preferred config format.

If for some reason, you still want to store master keys in the config format use the --retain or -r option.

Account Keys

Account Keys are personal access tokens that can be used to create a new Parse app and list properties and keys for the given Parse app. You can configure the command line tool to use account keys to perform various actions like creating a new app and fetching master key for an app when required (for instance, when performing deploy, fetching logs, etc.).

As noted in this section, we'll not store master keys in config files anymore. Instead we'll fetch them as needed.

What does this mean for a regular deploy flow?

If you want to perform a deploy, you'll need the master key. Master key for the selected app can be fetched by using the apps API. But apps API requires user authentication in the form of (email, password) with which you registered for the Parse account, or a valid account key (created for that account).

If you do not configure account keys, you'll be prompted to enter an email & password. This disruption in the flow (for all commands requiring master key), can easily be averted by configuring an account key using the parse configure accountkey command.

$ parse configure accountkey
Input your account key or press enter to generate a new one.
Account Key: ${ACCOUNT_KEY}
Successfully stored account key for: "${ACCOUNT_EMAIL}".

Account keys you configure are stored in ${HOME}/.parse/netrc

You can configure multiple account keys, one for each Parse account (i.e., each unique email). This is helpful if you have multiple Parse accounts.

However, if you have just one Parse account, then it's very useful to configure a default account key.

$ parse configure accountkey -d
Input your account key or press enter to generate a new one.
Account Key: ${ACCOUNT_KEY}
Successfully stored default account key.

Multiple Account Keys

Let's say you have two Parse accounts: ME (your Parse account) and WORK (account you are a collaborator on). You are working on app-ME (owned by ME) and app-WORK (owned by WORK). Now if you want to deploy code to app-ME, you need to use the account key for ME. To deploy to app-WORK, you'll need to use the account key for WORK.

How would the command line tool know which account key to use?

You tell it what to use by configuring an email for the project using parse configure email ${ACCOUNT_EMAIL}. This gets stored in the project config: .parse.project and any time you need to fetch the master key, this config setting helps identify which account key to use. If it does not find any account key for the configured email, it will try to use the default account key instead.

So, it is always a good idea to configure your default account key.

It is slightly different when you want to create an app or list properties for an app belonging to a specific account. You can select the Parse account to use by providing the environment variable: PARSER_EMAIL

For instance, in Mac or Linux

PARSER_EMAIL=${ACCOUNT_EMAIL} parse new
PARSER_EMAIL=${ACCOUNT_EMAIL} parse list

In windows, (on powershell)

cmd /C set "PARSER_EMAIL=%ACCOUNT_EMAIL%" && parse new
cmd /C set "PARSER_EMAIL=%ACCOUNT_EMAIL%" && parse list

Alternatively, you can just set it as a system wide environment variable, so any subsequent parse commands including: deploy, logs, etc. also use the same Parse account.

How to continuously deploy to your Parse app from Github

It is very easy to deploy to your Parse app on every push to Github. For this we use Travis CI, a popular continuous integration framework.

Steps to follow:

  • Set up your github repository with travis: you can use the .travis.yml in this repo as a starting point.
  • Add your Parse account key as a secret environment variable.

That is all there is to it! If you like to learn more, please take a look at this github repository.

Webhooks

You can use the parse configure hooks command to configure webhooks for your Parse app very easily.

All you have to do is write a json file with the following rules:

# content of the file is an array of operations
{"hooks": [OPERATION]}

# where OPERATION is any of
OPERATION ->
{"op": "put", "function": {"functionName": "name", "url": "https_url"}}
{"op": "post", "function": {"functionName": "name", "url": "https_url"}}
{"op": "delete", "function": {"functionName": "name"}}

{"op": "put", "trigger": {"className": "cname", "triggerName": "tname", "url":"https_url"}}
{"op": "post", "trigger": {"className": "cname", "triggerName": "tname", "url":"https_url"}}
{"op": "delete", "trigger": {"className": "cname", "triggerName": "tname"}}

Content of the json file is {"hooks" : [...]}, where the operation is a json object indicating an op like: put, post, delete; on a function or trigger webhook.

Example usage:

# you can provide a config file name to use
parse configure hooks webhooks.json

# you can also uses piping to provide configuration through stdin
cat webhooks.json| parse configure hooks

NOTE: The configure hooks command handles common error cases like, creating an already existing webhook and modifying or deleting a non-existant webhook. So for instance, if you try to modify a non-existant webhooks, it will create it for you. If you are trying to create an existant webhook, it will modify the current webhook with the url provided in configuration. It will skip deletion of non-existant webhooks.

This comes in pretty handy if you want to automate webhook creation or want to dynamically modify them.

A sample webhooks config file looks like follows:

{
  "hooks": [
    {
      "op": "post",
      "function": {
        "functionName": "hello",
        "url": "/webhooks/function_hello"
      }
    },
    {
      "op": "post",
      "trigger": {
        "className": "TestObject",
        "triggerName": "beforeSave",
        "url": "/webhooks/beforeSave_TestObject"
      }
    },
    {
      "op": "post",
      "trigger": {
        "className": "TestObject",
        "triggerName": "afterSave",
        "url": "/webhooks/afterSave_TestObject"
      }
    },
    {
      "op": "post",
      "trigger": {
        "className": "TestObject",
        "triggerName": "beforeDelete",
        "url": "/webhooks/beforeDelete_TestObject"
      }
    },
    {
      "op": "post",
      "trigger": {
        "className": "TestObject",
        "triggerName": "afterDelete",
        "url": "/webhooks/afterDelete_TestObject"
      }
    }
  ]
}

Note that we did not provide the full https url, instead just provided the relative path. You can register these webhooks using the configure hooks -b command.

parse configure hooks -b https://example.ngrok.io

Here we used the -b or --base option to pass the base webhook url to use. The registered hello function would look like:

Function name: "hello", URL: "https://example.ngrok.io/webhooks/function_hello"
Want to contribute to this doc? Edit this section.
Was this section helpful? NO YES Thanks for your feedback, we’re always working to make our docs better.

Command Line Tool with Heroku

We've partnered with Heroku to create a smooth experience for you to run your server code on either Heroku or the Parse Cloud. Before adding native support for Heroku to the Parse Tools, you could accomplish the same thing, but needed to follow many steps like, creating the Heroku app, deploying to it, book keeping between a Parse app and its corresponding Heroku app, registering the webhooks, etc. With this integration all these steps are managed by the Parse Tool, for most simple use cases you can run your server code on Heroku being totally agnostic about Heroku tools. Please refer to the announcement here for more details.

How does it work?

When you want to add server code to your Parse app, you can choose either Heroku or Parse. If you choose Heroku, we'll automatically create a Heroku app for you and use it as the backend for running your server code. You'll be able to deploy to the Heroku app, view its logs and current releases, and rollback a release, just as you do with Parse Cloud Code. All you have to do is link your Heroku account to Parse (just visit this page and click the β€œLink Heroku” button at the bottom). Note that by linking your Heroku account to your Parse account, you're only authorizing Parse to create Heroku apps on your behalf. Parse does not have any other permissions. For instance, we will not be able to list all your Heroku apps, change settings for Heroku apps not created through Parse, or change your Heroku account settings. This ensures additional security and makes the linking more trustworthy to even the advanced Heroku users.

What happens under the hood

Once you link Heroku to Parse, we'll have an access token that can create apps on Heroku. For any app thus created, Heroku provides us with an app-scoped access token, that can perform various actions on the created Heroku app and it alone. We store these credentials in our database at Parse. Whenever, you or a collaborator of your Parse app performs an action on server code, we generate a unique access token and use it to perform actions like deploying, fetching logs, and viewing releases of your Heroku app.

Can I link an existing Heroku app to a Parse app?

Unfortunately, this is not possible at this time. If you have an existing Heroku app for your webhooks, you can continue to use the Heroku CLI to deploy to it according to our blog post.

Installation

Mac and Linux

In Mac OS X and Linux/Unix environments, you can get the parse command line tool by running this command:

curl -s https://www.parse.com/downloads/cloud_code/installer.sh | sudo /bin/bash

This installs a tool named "parse" to /usr/local/bin/parse. There's no other junk, so to uninstall, just delete that file. This will also update your command line tool if you already have it installed.

Windows

The Parse command line tool for Windows is available here. Download the Windows executable named: parse.exe at this link. Note that this is not an installer, it is just a plain Windows executable.

Creating a Parse app backed by Heroku

You can create a new Parse app using parse new. It asks you a series of questions and at the end of it, you will have a new Parse app (with the given name), linked to a Heroku app where your server code will be run. You can test that everything works by performing parse deploy and executing the curl command printed at the end of output.

$ parse new
Would you like to create a new app, or add Cloud Code to an existing app?
Type "(n)ew" or "(e)xisting": n
Please choose a name for your Parse app.
Note that this name will appear on the Parse website,
but it does not have to be the same as your mobile app's public name.
Name: MyHerokuApp
Which of these providers would you like use for running your server code:
1) Heroku (https://www.heroku.com)
2) Parse  (https://parse.com/docs/cloudcode/guide)
Type 1 or 2 to make a selection: 1

Let's create a new Heroku app in which server code will be run.
The Heroku app will be named: "myherokuapp-bpru0l-4485"
Note that this can be changed later using Heroku API or Dashboard.

Awesome! Now it's time to set up some server code for the app: "MyHerokuApp",
Next we will create a directory to hold your server code.
Please enter the name to use for this directory,
or hit ENTER to use "MyHerokuApp" as the directory name.

Directory Name:

You can either set up a blank project or create a sample Node.js project.
Please type "(b)lank" if you wish to setup a blank project, otherwise press ENTER:
Successfully configured email for current project to: "${PARSER_EMAIL}"
Your server code has been created at ${CUR_DIR}/${CODE_DIR}.

This includes a "Hello world" cloud function, so once you deploy,
you can test that it works, with the printed curl command.

Next, you might want to deploy this code with:

    parse deploy

Once deployed you can test that it works by running:
curl -X POST \
 -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
 -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
 -H "Content-Type: application/json" \
 -d '{}' \
 https://api.parse.com/1/functions/hello

Setting up server code on Heroku

You can set up server code on Heroku for an existing Parse app very easily using parse new command. It does not matter whether the given Parse app already has Cloud Code or not.

$ parse new
Would you like to create a new app, or add Cloud Code to an existing app?
Type "(n)ew" or "(e)xisting": e
1:    MyApp
2:    MyOtherApp
Select an App to add to config: 1
Which of these providers would you like use for running your server code:
1) Heroku (https://www.heroku.com)
2) Parse  (https://parse.com/docs/cloudcode/guide)
Type 1 or 2 to make a selection: 1

Let's create a new Heroku app in which server code will be run.
The Heroku app will be named: "myapp-9j6adf-8139"
Note that this can be changed later using Heroku API or Dashboard.

Awesome! Now it's time to set up some Cloud Code for the app: "MyApp",
Next we will create a directory to hold your Cloud Code.
Please enter the name to use for this directory,
or hit ENTER to use "MyApp" as the directory name.

Directory Name: myapp

You can either set up a blank project or create a sample Node.js project.
Please type "(b)lank" if you wish to setup a blank project, otherwise press ENTER:
Successfully configured email for current project to: "${PARSER_EMAIL}"
Your server code has been created at ${CUR_DIR}/${CODE_DIR}.

This includes a "Hello world" cloud function, so once you deploy,
you can test that it works, with the printed curl command.

Next, you might want to deploy this code with:

    parse deploy

Once deployed you can test that it works by running:
curl -X POST \
 -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
 -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
 -H "Content-Type: application/json" \
 -d '{}' \
 https://api.parse.com/1/functions/hello

Note: This flow assumes that you already set up account keys. See also multiple account keys, if you are developing on apps belonging to different Parse accounts.

The myapp directory looks like:

myapp
β”œβ”€β”€ .parse.local
β”œβ”€β”€ .parse.project
β”œβ”€β”€ Procfile
β”œβ”€β”€ README.md
β”œβ”€β”€ cloud
β”‚   └── main.js
β”œβ”€β”€ package.json
β”œβ”€β”€ public
β”‚   └── index.html
β”œβ”€β”€ scripts
β”‚   β”œβ”€β”€ record-webhooks.js
β”‚   └── register-webhooks.js
└── server.js

.parse.local and .parse.project are config files that store application info, and project level info, respectively. The cloud directory stores your server code, and the public directory stores any static content that you want to host on Heroku. In the cloud directory, you'll typically just be editing main.js, which stores all the server side logic for your app.

For now, just check that these files were created successfully. If you're using source control, you can check all of these files in.

We recommend using source control to check in all of these files. If you're not already set up with source control, try this tutorial from GitHub.

The same code can be deployed to multiple different applications. This is useful so that you can have separate "development" and "production" applications. Then you test the code on a development application before launching it in production.

The first application that is added (by the new command) will be the default application for all command line operations. All commands except new take an optional app name that the command will be performed on.

Deploying

What does deploy mean?

When you execute parse deploy we will deploy your code or config changes to the Heroku app that runs server code for your Parse app. For node.js, if you use our middleware, we have a post install script that automatically registers webhooks for your Parse app. If you choose to not use our middleware or write server code in a language that does not yet have Parse middleware support, you can just provide a webhooks.json file in the same directory, and the Command Line Tool will automatically register these webhooks with Parse. Note that this is a very important step because Parse is only aware of those endpoints (of the Heroku app) that you register as webhooks with Parse. For more information on the webhooks.json file format please read the docs. This functionality is very useful even if you are an advanced Heroku user. Until now, you had to perform a deploy on Heroku via git push heroku master and also manage the registration of webhooks with Parse. But with this functionality, you do both in just one step - saving the context switch!

To deploy a new release, run parse deploy from the command line:

This deploys the code (in cloud and public) for the Heroku app corresponding to the default target which is the first app that was added or the one you set using parse default. You can choose to deploy to a different target by adding the target as an argument to deploy:

$ parse deploy
Successfully set the following vars to heroku:
HOOKS_URL https://myherokuapp-9j6adf-8139.herokuapp.com/
PARSE_APP_ID ************************************itMu
PARSE_MASTER_KEY ************************************RZ3Z
PARSE_WEBHOOK_KEY ************************************i8Ja
Initialized empty Git repository in ${CUR_DIR}/${CODE_DIR}/.git/
...
remote: Building source:
...
remote:        > Parse-Cloud-Code-Heroku@1.0.0 postinstall /tmp/build_e8aa01307d875739c887afce4663e3a2
remote:        > NODE_PATH=$NODE_PATH:./lib node scripts/register-webhooks.js
remote:        Hooks registration completed.
remote:        Registered function for: hello
remote:        jsonfile@2.2.3 node_modules/jsonfile
...
remote: Verifying deploy... done.
...

$ parse functions
The following cloudcode or webhook functions are associated with this app:
Function name: "hello", URL: "https://myherokuapp-9j6adf-8139.herokuapp.com/webhooks/function_hello"

You can add release notes to the deploy with the -d or --description option

Note that deploying to Heroku is equivalent to pushing to the Heroku git repository for the linked Heroku app. If the contents of the code have changed significantly, git can reject such changes. You can override this behavior with the -f or --force flag. Providing this flag forces a deploy in any case.

$ parse deploy
Successfully set the following vars to heroku:
PARSE_MASTER_KEY ************************************RZ3Z
PARSE_WEBHOOK_KEY ************************************i8Ja
HOOKS_URL https://myherokuapp-9j6adf-8139.herokuapp.com/
PARSE_APP_ID ************************************itMu
To https://:${HEROKU_OAUTH_ACCESS_TOKEN}@git.heroku.com/myherokuapp-9j6adf-8139.git
 ! [rejected]        master -> master (non-fast-forward)
error: failed to push some refs to 'https://:${HEROKU_OAUTH_ACCESS_TOKEN}@git.heroku.com/myherokuapp-9j6adf-8139.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
Unable to push to git remote: git.heroku.com/myherokuapp-9j6adf-8139.git

When embedding parse deploy within other scripts (such as in an automated testing/deploy environment) you can rely on the exit code from the Parse command line tool to indicate whether the command succeded. It will have an exit code of 0 on success and a non-zero exit code when the deploy failed.

A Simple Function

Following ancient tradition, let's see how to run the simplest possible function in the cloud. If you take a look at cloud/main.js, you'll see an example function that just returns a string:

Parse.Cloud.define("hello", function(request, response) {
  response.success("Hello world!");
});

To deploy the code from your machine to the Parse Cloud, run this from the command line:

$ parse deploy

Once deployed, this function can be called from any of the SDKs or the command line like so:

// Android SDK
ParseCloud.callFunctionInBackground("hello", new HashMap<String, Object>(), new FunctionCallback<String>() {
  void done(String result, ParseException e) {
    if (e == null) {
      // result is "Hello world!"
    }
  }
});
// iOS, OS X, tvOS, watchOS SDK: Objective-C
[PFCloud callFunctionInBackground:@"hello"
                   withParameters:@{}
                            block:^(NSString *result, NSError *error) {
   if (!error) {
     // result is @"Hello world!"
   }
}];
// iOS, OS X, tvOS, watchOS SDK: Swift
PFCloud.callFunctionInBackground("hello", withParameters: nil) {
  (response: AnyObject?, error: NSError?) -> Void in
  let responseString = response as? String
}
// PHP SDK
$result = ParseCloud::run("hello", []);
// .NET SDK
ParseCloud.CallFunctionAsync<IDictionary<string, object>>("hello", new Dictionary<string, object>()).ContinueWith(t => {
  var result = t.Result;
// result is "Hello world!"
});
// JavaScript SDK
Parse.Cloud.run('hello', {}, {
  success: function(result) {
    // result is 'Hello world!'
  },
  error: function(error) {
  }
});
# REST API, command line
curl -X POST \
 -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
 -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
 -H "Content-Type: application/json" \
 -d '{}' \
 https://api.parse.com/1/functions/hello

You should see this response:

{ "result": "Hello world!" }

Congratulations! You've successfully deployed and run your server code, hosted on Heroku.

This is a good time to play around with the deployment cycle. Try changing "Hello world!" to a different string, then deploy and run the function again to get a different result. You can use the Parse node npm module and the middleware to write the server side logic for your Parse app.

Local Development

Using popular server side programming environments like node.js, ruby on rails, django and others, makes it easier to test your changes locally, since you can run the full server side environment on your local machine.

You can easily test local changes using ngrok and the Hooks API. Ngrok allows you to expose a service running on your local machine, through a public URL protected by HTTPS. If you run an HTTP server on your local machine listening on port 5000, you can then run ngrok http 5000 and configure your webhooks using the provided URL. It is also possible to debug your server code while it runs.

Once you’ve got ngrok running, you can now configure your webhooks with Parse. This can be done from the dashboard, or through the Hooks API.

Further, you can use the parse configure hooks command to configure webhooks for your Parse app very easily. All you have to do is write a json file with the following rules:

# content of the file is an array of operations
{"hooks": [OPERATION]}

# where OPERATION is any of
OPERATION ->
{"op": "put", "function": {"functionName": "name", "url": "https_url"}}
{"op": "post", "function": {"functionName": "name", "url": "https_url"}}
{"op": "delete", "function": {"functionName": "name"}}

{"op": "put", "trigger": {"className": "cname", "triggerName": "tname", "url":"https_url"}}
{"op": "post", "trigger": {"className": "cname", "triggerName": "tname", "url":"https_url"}}
{"op": "delete", "trigger": {"className": "cname", "triggerName": "tname"}}

Content of the json file is {"hooks" : [...]}, where the operation is a json object indicating an op like: put, post, delete; on a function or trigger webhook.

NOTE: For node.js, when using Parse middleware, you can just run node scripts/record-webhooks.js to generate the webhooks json configuration file.

Example usage:

# you can provide a config file name to use
parse configure hooks webhooks.json

# you can also uses piping to provide configuration through stdin
cat webhooks.json| parse configure hooks

NOTE: The configure hooks command handles common error cases like, creating an already existing webhook and modifying or deleting a non-existant webhook. So for instance, if you try to modify a non-existant webhooks, it will create it for you. If you are trying to create an existant webhook, it will modify the current webhook with the url provided in configuration. It will skip deletion of non-existant webhooks.

Adding a New Target

You can add a new Parse app (with server code running on Heroku) as a target by running the add command.

If the Parse app you want to add to the config is not yet linked to a Heroku app, then we'll create a new Heroku app for you

$ parse add
1: MyApp
2: MyOtherApp
Select an App to add to config: 2
Let's create a new Heroku app in which server code will be run.
The Heroku app will be named: "myotherapp-uygdrx-2101"
Note that this can be changed later using Heroku API or Dashboard.

Written config for "MyOtherApp"

If the Parse app you want to add is already linked to a Heroku app, we'll just add it to config

$ parse add
1: MyApp
2: MyOtherApp
Select an App to add to config: 2
Written config for "MyOtherApp"

The add command takes an optional argument which is an alias to assign to the application that can be used instead of the app name.

Setting the Default App

parse deploy, parse logs, parse rollback, and parse releases use the default app to be run against the commands. parse default allows you to change this default app.

$ parse default MyApp
Default app set to MyApp.
$ parse default
Current default app is MyApp.

Rolling Back

You can roll back a release using parse rollback. Just like with parse deploy, you can specify an optional target argument.

$ parse rollback
Rollback to v1
Current version: 2

This rolls back to the previous version of the code. You can also specify the release name to roll back to by using the -r or --release option.

Reading the Logs

Every deploy, rollback, and activation of server code is logged. You can retrieve the end of logs using the parse logs command.

  • -t, --tail=false: Emulates tail -f and streams new messages from the server
  • -n, --num=50: The number of the messages to display
$ parse logs
2015-10-19T06:02:36.359126+00:00 heroku[api]: Enable Logplex by parser@email.com
2015-10-19T06:02:36.359180+00:00 heroku[api]: Release v2 created by parser@email.com
2015-10-19T06:13:03.671645+00:00 heroku[api]: Set HOOKS_URL, PARSE_APP_ID, PARSE_MASTER_KEY, PARSE_WEBHOOK_KEY config vars by 11211@oauth.heroku.com
2015-10-19T06:13:03.671645+00:00 heroku[api]: Release v3 created by 11211@oauth.heroku.com
2015-10-19T06:13:26.898011+00:00 heroku[api]: Scale to web=1 by 11211@oauth.heroku.com
2015-10-19T06:13:26.963451+00:00 heroku[api]: Deploy 7ceb235 by 11211@oauth.heroku.com
2015-10-19T06:13:26.963451+00:00 heroku[api]: Release v4 created by 11211@oauth.heroku.com
2015-10-19T06:13:27.037831+00:00 heroku[slug-compiler]: Slug compilation started
2015-10-19T06:13:27.037840+00:00 heroku[slug-compiler]: Slug compilation finished
2015-10-19T06:13:28.543433+00:00 heroku[web.1]: Starting process with command `node server.js`
2015-10-19T06:13:31.007200+00:00 app[web.1]: Cloud Code Webhooks server running on port 38751.
2015-10-19T06:13:32.155941+00:00 heroku[web.1]: State changed from starting to up
2015-10-19T06:15:37.176606+00:00 heroku[api]: Rollback to v3 by 11211@oauth.heroku.com
2015-10-19T06:15:37.176666+00:00 heroku[api]: Release v5 created by 11211@oauth.heroku.com

Listing Releases

You can list the known set of releases on the linked Heroku app with the releases command.

$ parse releases
Version             Update At                      Description
4                   2015-10-19 06:13:26 +0000 UTC  Deployed by: "11211@oauth.heroku.com". Deploy 7ceb235

3                   2015-10-19 06:13:03 +0000 UTC  Deployed by: "11211@oauth.heroku.com". Set HOOKS_URL, PARSE_APP_ID, PARSE_MASTER_KEY, PARSE_WEBHOOK_KEY config vars

2                   2015-10-19 06:02:36 +0000 UTC  Deployed by: "parser@gmail.com". Enable Logplex

1                   2015-10-19 06:02:35 +0000 UTC  Deployed by: "parser@email.com". Initial release
...
Want to contribute to this doc? Edit this section.
Was this section helpful? NO YES Thanks for your feedback, we’re always working to make our docs better.