Products Customers Pricing Docs Help Blog
JavaScript Guide
Browse All Platforms More Platforms

Getting Started

If you haven't set up your project yet, please head over to the QuickStart guide to get up and running. You can also check out our API Reference for more detailed information about our SDK.

The Parse platform provides a complete backend solution for your mobile application. Our goal is to totally eliminate the need for writing server code or maintaining servers.

If you're looking to build a React application with Parse, we provide a special library for that. All of the documentation is available at the GitHub repo.

Our JavaScript SDK is based on the popular Backbone.js framework. It is compatible with existing Backbone applications with minimal changes on your part. Our goal is to minimize configuration and let you quickly start building your JavaScript and HTML5 app on Parse.

Our SDK supports Firefox 23+, Chrome 17+, Safari 5+, and IE 10. IE 8 and IE 9 are supported only for apps that are hosted with HTTPS.

On Parse, you create an App for each of your mobile applications. Each App has its own application id and JavaScript client key that you apply to your web app. Your account on Parse can accommodate multiple Apps. This is useful even if you have one application, since you can deploy different versions for test and production.

Our JavaScript SDK does not require any external libraries. The only exception is the Parse.View class, which requires that you provide jQuery or a jQuery compatible $ method.

Most of the examples below use success and error callbacks for simplicity. If you're familiar with JavaScript Promises or would like to learn how to avoid writing nested code, scroll down to the Promises section.

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.

Objects

Parse.Object

Storing data on Parse is built around Parse.Object. Each Parse.Object contains key-value pairs of JSON-compatible data. This data is schemaless, which means that you don't need to specify ahead of time what keys exist on each Parse.Object. You simply set whatever key-value pairs you want, and our backend will store it.

For example, let's say you're tracking high scores for a game. A single Parse.Object could contain:

score: 1337, playerName: "Sean Plott", cheatMode: false

Keys must be alphanumeric strings. Values can be strings, numbers, booleans, or even arrays and dictionaries - anything that can be JSON-encoded.

Each Parse.Object is an instance of a specific subclass with a class name that you can use to distinguish different sorts of data. For example, we could call the high score object a GameScore. We recommend that you NameYourClassesLikeThis and nameYourKeysLikeThis, just to keep your code looking pretty.

To create a new subclass, use the Parse.Object.extend method. Any Parse.Query will return instances of the new class for any Parse.Object with the same classname. If you're familiar with Backbone.Model, then you already know how to use Parse.Object. It's designed to be a drop-in replacement.

// Simple syntax to create a new subclass of Parse.Object.
var GameScore = Parse.Object.extend("GameScore");

// Create a new instance of that class.
var gameScore = new GameScore();

// Alternatively, you can use the typical Backbone syntax.
var Achievement = Parse.Object.extend({
  className: "Achievement"
});

You can add additional methods and properties to your subclasses of Parse.Object.


// A complex subclass of Parse.Object
var Monster = Parse.Object.extend("Monster", {
  // Instance methods
  hasSuperHumanStrength: function () {
    return this.get("strength") > 18;
  },
  // Instance properties go in an initialize method
  initialize: function (attrs, options) {
    this.sound = "Rawr"
  }
}, {
  // Class methods
  spawn: function(strength) {
    var monster = new Monster();
    monster.set("strength", strength);
    return monster;
  }
});

var monster = Monster.spawn(200);
alert(monster.get('strength'));  // Displays 200.
alert(monster.sound); // Displays Rawr.

Saving Objects

Let's say you want to save the GameScore described above to the Parse Cloud. The interface is similar to a Backbone.Model, including the save method:

var GameScore = Parse.Object.extend("GameScore");
var gameScore = new GameScore();

gameScore.set("score", 1337);
gameScore.set("playerName", "Sean Plott");
gameScore.set("cheatMode", false);

gameScore.save(null, {
  success: function(gameScore) {
    // Execute any logic that should take place after the object is saved.
    alert('New object created with objectId: ' + gameScore.id);
  },
  error: function(gameScore, error) {
    // Execute any logic that should take place if the save fails.
    // error is a Parse.Error with an error code and message.
    alert('Failed to create new object, with error code: ' + error.message);
  }
});

After this code runs, you will probably be wondering if anything really happened. To make sure the data was saved, you can look at the Data Browser in your app on Parse. You should see something like this:

objectId: "xWMyZ4YEGZ", score: 1337, playerName: "Sean Plott", cheatMode: false,
createdAt:"2011-06-10T18:33:42Z", updatedAt:"2011-06-10T18:33:42Z"

There are two things to note here. You didn't have to configure or set up a new Class called GameScore before running this code. Your Parse app lazily creates this Class for you when it first encounters it.

There are also a few fields you don't need to specify that are provided as a convenience. objectId is a unique identifier for each saved object. createdAt and updatedAt represent the time that each object was created and last modified in the cloud. Each of these fields is filled in by Parse, so they don't exist on a Parse.Object until a save operation has completed.

If you prefer, you can set attributes directly in your call to save instead.

var GameScore = Parse.Object.extend("GameScore");
var gameScore = new GameScore();

gameScore.save({
  score: 1337,
  playerName: "Sean Plott",
  cheatMode: false
}, {
  success: function(gameScore) {
    // The object was saved successfully.
  },
  error: function(gameScore, error) {
    // The save failed.
    // error is a Parse.Error with an error code and message.
  }
});

Retrieving Objects

Saving data to the cloud is fun, but it's even more fun to get that data out again. If you have the objectId, you can retrieve the whole Parse.Object using a Parse.Query:

var GameScore = Parse.Object.extend("GameScore");
var query = new Parse.Query(GameScore);
query.get("xWMyZ4YEGZ", {
  success: function(gameScore) {
    // The object was retrieved successfully.
  },
  error: function(object, error) {
    // The object was not retrieved successfully.
    // error is a Parse.Error with an error code and message.
  }
});

To get the values out of the Parse.Object, use the get method.

var score = gameScore.get("score");
var playerName = gameScore.get("playerName");
var cheatMode = gameScore.get("cheatMode");

The three special values are provided as properties:

var objectId = gameScore.id;
var updatedAt = gameScore.updatedAt;
var createdAt = gameScore.createdAt;

If you need to refresh an object you already have with the latest data that is in the Parse Cloud, you can call the fetch method like so:

myObject.fetch({
  success: function(myObject) {
    // The object was refreshed successfully.
  },
  error: function(myObject, error) {
    // The object was not refreshed successfully.
    // error is a Parse.Error with an error code and message.
  }
});

Updating Objects

Updating an object is simple. Just set some new data on it and call the save method. For example:

// Create the object.
var GameScore = Parse.Object.extend("GameScore");
var gameScore = new GameScore();

gameScore.set("score", 1337);
gameScore.set("playerName", "Sean Plott");
gameScore.set("cheatMode", false);
gameScore.set("skills", ["pwnage", "flying"]);

gameScore.save(null, {
  success: function(gameScore) {
    // Now let's update it with some new data. In this case, only cheatMode and score
    // will get sent to the cloud. playerName hasn't changed.
    gameScore.set("cheatMode", true);
    gameScore.set("score", 1338);
    gameScore.save();
  }
});

Parse automatically figures out which data has changed so only "dirty" fields will be sent to the Parse Cloud. You don't need to worry about squashing data that you didn't intend to update.

Counters

The above example contains a common use case. The "score" field is a counter that we'll need to continually update with the player's latest score. Using the above method works but it's cumbersome and can lead to problems if you have multiple clients trying to update the same counter.

To help with storing counter-type data, Parse provides methods that atomically increment (or decrement) any number field. So, the same update can be rewritten as:

gameScore.increment("score");
gameScore.save();

You can also increment by any amount by passing in a second argument to increment. When no amount is specified, 1 is used by default.

Arrays

To help with storing array data, there are three operations that can be used to atomically change an array associated with a given key:

For example, we can add items to the set-like "skills" field like so:

gameScore.addUnique("skills", "flying");
gameScore.addUnique("skills", "kungfu");
gameScore.save();

Note that it is not currently possible to atomically add and remove items from an array in the same save. You will have to call save in between every different kind of array operation.

Destroying Objects

To delete an object from the cloud:

myObject.destroy({
  success: function(myObject) {
    // The object was deleted from the Parse Cloud.
  },
  error: function(myObject, error) {
    // The delete failed.
    // error is a Parse.Error with an error code and message.
  }
});

You can delete a single field from an object with the unset method:

// After this, the playerName field will be empty
myObject.unset("playerName");

// Saves the field deletion to the Parse Cloud
myObject.save();

Relational Data

Objects may have relationships with other objects. For example, in a blogging application, a Post object may have many Comment objects. Parse supports all kind of relationships, including one-to-one, one-to-many, and many-to-many.

One-to-One and One-to-Many Relationships

One-to-one and one-to-many relationships are modeled by saving a Parse.Object as a value in the other object. For example, each Comment in a blogging app might correspond to one Post.

To create a new Post with a single Comment, you could write:

// Declare the types.
var Post = Parse.Object.extend("Post");
var Comment = Parse.Object.extend("Comment");

// Create the post
var myPost = new Post();
myPost.set("title", "I'm Hungry");
myPost.set("content", "Where should we go for lunch?");

// Create the comment
var myComment = new Comment();
myComment.set("content", "Let's do Sushirrito.");

// Add the post as a value in the comment
myComment.set("parent", myPost);

// This will save both myPost and myComment
myComment.save();

Internally, the Parse framework will store the referred-to object in just one place, to maintain consistency. You can also link objects using just their objectIds like so:

var post = new Post();
post.id = "1zEcyElZ80";

myComment.set("parent", post);

By default, when fetching an object, related Parse.Objects are not fetched. These objects' values cannot be retrieved until they have been fetched like so:

var post = fetchedComment.get("parent");
post.fetch({
  success: function(post) {
    var title = post.get("title");
  }
});

Many-to-Many Relationships

Many-to-many relationships are modeled using Parse.Relation. This works similar to storing an array of Parse.Objects in a key, except that you don't need to fetch all of the objects in a relation at once. In addition, this allows Parse.Relation to scale to many more objects than the array of Parse.Object approach. For example, a User may have many Posts that she might like. In this case, you can store the set of Posts that a User likes using relation. In order to add a Post to the "likes" list of the User, you can do:

var user = Parse.User.current();
var relation = user.relation("likes");
relation.add(post);
user.save();

You can remove a post from a Parse.Relation:

relation.remove(post);
user.save();

You can call add and remove multiple times before calling save:

relation.remove(post1);
relation.remove(post2);
user.save();

You can also pass in an array of Parse.Object to add and remove:

relation.add([post1, post2, post3]);
user.save();

By default, the list of objects in this relation are not downloaded. You can get a list of the posts that a user likes by using the Parse.Query returned by query. The code looks like:

relation.query().find({
  success: function(list) {
    // list contains the posts that the current user likes.
  }
});

If you want only a subset of the Posts, you can add extra constraints to the Parse.Query returned by query like this:

var query = relation.query();
query.equalTo("title", "I'm Hungry");
query.find({
  success:function(list) {
    // list contains post liked by the current user which have the title "I'm Hungry".
  }
});

For more details on Parse.Query, please look at the query portion of this guide. A Parse.Relation behaves similar to an array of Parse.Object for querying purposes, so any query you can do on an array of objects, you can do on a Parse.Relation.

Data Types

So far we've used values with type String, Number, and Parse.Object. Parse also supports JavaScript Dates and null.

You can nest JavaScript Objects and JavaScript Arrays to store more structured data within a single Parse.Object.

Some examples:

var number = 42;
var string = "the number is " + number;
var date = new Date();
var array = [string, number];
var object = { number: number, string: string };

var BigObject = Parse.Object.extend("BigObject");
var bigObject = new BigObject();
bigObject.set("myNumber", number);
bigObject.set("myString", string);
bigObject.set("myDate", date);
bigObject.set("myArray", array);
bigObject.set("myObject", object);
bigObject.set("myNull", null);
bigObject.save();

Parse.Objects should not exceed 128 kilobytes in size.

For more information about how Parse handles data, check out our documentation on Data.

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.

Queries

We've already seen how a Parse.Query with get can retrieve a single Parse.Object from Parse. There are many other ways to retrieve data with Parse.Query - you can retrieve many objects at once, put conditions on the objects you wish to retrieve, and more.

Basic Queries

In many cases, get isn't powerful enough to specify which objects you want to retrieve. Parse.Query offers different ways to retrieve a list of objects rather than just a single object.

The general pattern is to create a Parse.Query, put conditions on it, and then retrieve an Array of matching Parse.Objects using find. For example, to retrieve the scores that have a particular playerName, use the equalTo method to constrain the value for a key.

var GameScore = Parse.Object.extend("GameScore");
var query = new Parse.Query(GameScore);
query.equalTo("playerName", "Dan Stemkoski");
query.find({
  success: function(results) {
    alert("Successfully retrieved " + results.length + " scores.");
    // Do something with the returned Parse.Object values
    for (var i = 0; i < results.length; i++) {
      var object = results[i];
      alert(object.id + ' - ' + object.get('playerName'));
    }
  },
  error: function(error) {
    alert("Error: " + error.code + " " + error.message);
  }
});

Query Constraints

There are several ways to put constraints on the objects found by a Parse.Query. You can filter out objects with a particular key-value pair with notEqualTo:

query.notEqualTo("playerName", "Michael Yabuti");

You can give multiple constraints, and objects will only be in the results if they match all of the constraints. In other words, it's like an AND of constraints.

query.notEqualTo("playerName", "Michael Yabuti");
query.greaterThan("playerAge", 18);

You can limit the number of results by setting limit. By default, results are limited to 100, but anything from 1 to 1000 is a valid limit:

query.limit(10); // limit to at most 10 results

If you want exactly one result, a more convenient alternative may be to use first instead of using find.

var GameScore = Parse.Object.extend("GameScore");
var query = new Parse.Query(GameScore);
query.equalTo("playerEmail", "dstemkoski@example.com");
query.first({
  success: function(object) {
    // Successfully retrieved the object.
  },
  error: function(error) {
    alert("Error: " + error.code + " " + error.message);
  }
});

You can skip the first results by setting skip. This can be useful for pagination:

query.skip(10); // skip the first 10 results

For sortable types like numbers and strings, you can control the order in which results are returned:

// Sorts the results in ascending order by the score field
query.ascending("score");

// Sorts the results in descending order by the score field
query.descending("score");

For sortable types, you can also use comparisons in queries:

// Restricts to wins < 50
query.lessThan("wins", 50);

// Restricts to wins <= 50
query.lessThanOrEqualTo("wins", 50);

// Restricts to wins > 50
query.greaterThan("wins", 50);

// Restricts to wins >= 50
query.greaterThanOrEqualTo("wins", 50);

If you want to retrieve objects matching any of the values in a list of values, you can use containedIn, providing an array of acceptable values. This is often useful to replace multiple queries with a single query. For example, if you want to retrieve scores made by any player in a particular list:

// Finds scores from any of Jonathan, Dario, or Shawn
query.containedIn("playerName",
                  ["Jonathan Walsh", "Dario Wunsch", "Shawn Simon"]);

If you want to retrieve objects that do not match any of several values you can use notContainedIn, providing an array of acceptable values. For example if you want to retrieve scores from players besides those in a list:

// Finds scores from anyone who is neither Jonathan, Dario, nor Shawn
query.notContainedIn("playerName",
                     ["Jonathan Walsh", "Dario Wunsch", "Shawn Simon"]);

If you want to retrieve objects that have a particular key set, you can use exists. Conversely, if you want to retrieve objects without a particular key set, you can use doesNotExist.

// Finds objects that have the score set
query.exists("score");

// Finds objects that don't have the score set
query.doesNotExist("score");

You can use the matchesKeyInQuery method to get objects where a key matches the value of a key in a set of objects resulting from another query. For example, if you have a class containing sports teams and you store a user's hometown in the user class, you can issue one query to find the list of users whose hometown teams have winning records. The query would look like:

var Team = Parse.Object.extend("Team");
var teamQuery = new Parse.Query(Team);
teamQuery.greaterThan("winPct", 0.5);
var userQuery = new Parse.Query(Parse.User);
userQuery.matchesKeyInQuery("hometown", "city", teamQuery);
userQuery.find({
  success: function(results) {
    // results has the list of users with a hometown team with a winning record
  }
});

Conversely, to get objects where a key does not match the value of a key in a set of objects resulting from another query, use doesNotMatchKeyInQuery. For example, to find users whose hometown teams have losing records:

var losingUserQuery = new Parse.Query(Parse.User);
losingUserQuery.doesNotMatchKeyInQuery("hometown", "city", teamQuery);
losingUserQuery.find({
  success: function(results) {
    // results has the list of users with a hometown team with a losing record
  }
});

You can restrict the fields returned by calling select with a list of keys. To retrieve documents that contain only the score and playerName fields (and also special built-in fields such as objectId, createdAt, and updatedAt):

var GameScore = Parse.Object.extend("GameScore");
var query = new Parse.Query(GameScore);
query.select("score", "playerName");
query.find().then(function(results) {
  // each of results will only have the selected fields available.
});

The remaining fields can be fetched later by calling fetch on the returned objects:

query.first().then(function(result) {
  // only the selected fields of the object will now be available here.
  return result.fetch();
}).then(function(result) {
  // all fields of the object will now be available here.
});

Queries on Array Values

For keys with an array type, you can find objects where the key's array value contains 2 by:

// Find objects where the array in arrayKey contains 2.
query.equalTo("arrayKey", 2);

You can also find objects where the key's array value contains each of the elements 2, 3, and 4 with the following:

// Find objects where the array in arrayKey contains all of the elements 2, 3, and 4.
query.containsAll("arrayKey", [2, 3, 4]);

Queries on String Values

If you're trying to implement a generic search feature, we recommend taking a look at this blog post: Implementing Scalable Search on a NoSQL Backend.

Use startsWith to restrict to string values that start with a particular string. Similar to a MySQL LIKE operator, this is indexed so it is efficient for large datasets:

// Finds barbecue sauces that start with "Big Daddy's".
var query = new Parse.Query(BarbecueSauce);
query.startsWith("name", "Big Daddy's");

The above example will match any BarbecueSauce objects where the value in the "name" String key starts with "Big Daddy's". For example, both "Big Daddy's" and "Big Daddy's BBQ" will match, but "big daddy's" or "BBQ Sauce: Big Daddy's" will not.

Queries that have regular expression constraints are very expensive, especially for classes with over 100,000 records. Parse restricts how many such operations can be run on a particular app at any given time.

Relational Queries

There are several ways to issue queries for relational data. If you want to retrieve objects where a field matches a particular Parse.Object, you can use equalTo just like for other data types. For example, if each Comment has a Post object in its post field, you can fetch comments for a particular Post:

// Assume Parse.Object myPost was previously created.
var query = new Parse.Query(Comment);
query.equalTo("post", myPost);
query.find({
  success: function(comments) {
    // comments now contains the comments for myPost
  }
});

If you want to retrieve objects where a field contains a Parse.Object that matches a different query, you can use matchesQuery. Note that the default limit of 100 and maximum limit of 1000 apply to the inner query as well, so with large data sets you may need to construct queries carefully to get the desired behavior. In order to find comments for posts containing images, you can do:

var Post = Parse.Object.extend("Post");
var Comment = Parse.Object.extend("Comment");
var innerQuery = new Parse.Query(Post);
innerQuery.exists("image");
var query = new Parse.Query(Comment);
query.matchesQuery("post", innerQuery);
query.find({
  success: function(comments) {
    // comments now contains the comments for posts with images.
  }
});

If you want to retrieve objects where a field contains a Parse.Object that does not match a different query, you can use doesNotMatchQuery. In order to find comments for posts without images, you can do:

var Post = Parse.Object.extend("Post");
var Comment = Parse.Object.extend("Comment");
var innerQuery = new Parse.Query(Post);
innerQuery.exists("image");
var query = new Parse.Query(Comment);
query.doesNotMatchQuery("post", innerQuery);
query.find({
  success: function(comments) {
    // comments now contains the comments for posts without images.
  }
});

You can also do relational queries by objectId:

var post = new Post();
post.id = "1zEcyElZ80";
query.equalTo("post", post);

In some situations, you want to return multiple types of related objects in one query. You can do this with the include method. For example, let's say you are retrieving the last ten comments, and you want to retrieve their related posts at the same time:

var query = new Parse.Query(Comment);

// Retrieve the most recent ones
query.descending("createdAt");

// Only retrieve the last ten
query.limit(10);

// Include the post data with each comment
query.include("post");

query.find({
  success: function(comments) {
    // Comments now contains the last ten comments, and the "post" field
    // has been populated. For example:
    for (var i = 0; i < comments.length; i++) {
      // This does not require a network access.
      var post = comments[i].get("post");
    }
  }
});

You can also do multi level includes using dot notation. If you wanted to include the post for a comment and the post's author as well you can do:

query.include(["post.author"]);

You can issue a query with multiple fields included by calling include multiple times. This functionality also works with Parse.Query helpers like first and get.

Counting Objects

Caveat: Count queries are rate limited to a maximum of 160 requests per minute. They can also return inaccurate results for classes with more than 1,000 objects. Thus, it is preferable to architect your application to avoid this sort of count operation (by using counters, for example.)

If you just need to count how many objects match a query, but you do not need to retrieve all the objects that match, you can use count instead of find. For example, to count how many games have been played by a particular player:

var GameScore = Parse.Object.extend("GameScore");
var query = new Parse.Query(GameScore);
query.equalTo("playerName", "Sean Plott");
query.count({
  success: function(count) {
    // The count request succeeded. Show the count
    alert("Sean has played " + count + " games");
  },
  error: function(error) {
    // The request failed
  }
});

Compound Queries

If you want to find objects that match one of several queries, you can use Parse.Query.or method to construct a query that is an OR of the queries passed in. For instance if you want to find players who either have a lot of wins or a few wins, you can do:

var lotsOfWins = new Parse.Query("Player");
lotsOfWins.greaterThan("wins", 150);

var fewWins = new Parse.Query("Player");
fewWins.lessThan("wins", 5);

var mainQuery = Parse.Query.or(lotsOfWins, fewWins);
mainQuery.find({
  success: function(results) {
     // results contains a list of players that either have won a lot of games or won only a few games.
  },
  error: function(error) {
    // There was an error.
  }
});

You can add additional constraints to the newly created Parse.Query that act as an 'and' operator.

Note that we do not, however, support GeoPoint or non-filtering constraints (e.g. near, withinGeoBox, limit, skip, ascending/descending, include) in the subqueries of the compound query.

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.

Users

At the core of many apps, there is a notion of user accounts that lets users access their information in a secure manner. We provide a specialized user class called Parse.User that automatically handles much of the functionality required for user account management.

With this class, you'll be able to add user account functionality in your app.

Parse.User is a subclass of Parse.Object, and has all the same features, such as flexible schema, automatic persistence, and a key value interface. All the methods that are on Parse.Object also exist in Parse.User. The difference is that Parse.User has some special additions specific to user accounts.

Properties

Parse.User has several values that set it apart from Parse.Object:

We'll go through each of these in detail as we run through the various use cases for users.

Signing Up

The first thing your app will do is probably ask the user to sign up. The following code illustrates a typical sign up:

var user = new Parse.User();
user.set("username", "my name");
user.set("password", "my pass");
user.set("email", "email@example.com");

// other fields can be set just like with Parse.Object
user.set("phone", "415-392-0202");

user.signUp(null, {
  success: function(user) {
    // Hooray! Let them use the app now.
  },
  error: function(user, error) {
    // Show the error message somewhere and let the user try again.
    alert("Error: " + error.code + " " + error.message);
  }
});

This call will asynchronously create a new user in your Parse App. Before it does this, it also checks to make sure that both the username and email are unique. Also, it securely hashes the password in the cloud using bcrypt. We never store passwords in plaintext, nor will we ever transmit passwords back to the client in plaintext.

Note that we used the signUp method, not the save method. New Parse.Users should always be created using the signUp method. Subsequent updates to a user can be done by calling save.

If a signup isn't successful, you should read the error object that is returned. The most likely case is that the username or email has already been taken by another user. You should clearly communicate this to your users, and ask them try a different username.

You are free to use an email address as the username. Simply ask your users to enter their email, but fill it in the username property — Parse.User will work as normal. We'll go over how this is handled in the reset password section.

Logging In

Of course, after you allow users to sign up, you need to let them log in to their account in the future. To do this, you can use the class method logIn.

Parse.User.logIn("myname", "mypass", {
  success: function(user) {
    // Do stuff after successful login.
  },
  error: function(user, error) {
    // The login failed. Check error to see why.
  }
});

Verifying Emails

Enabling email verification in an application's settings allows the application to reserve part of its experience for users with confirmed email addresses. Email verification adds the emailVerified key to the Parse.User object. When a Parse.User's email is set or modified, emailVerified is set to false. Parse then emails the user a link which will set emailVerified to true.

There are three emailVerified states to consider:

  1. true - the user confirmed his or her email address by clicking on the link Parse emailed them. Parse.Users can never have a true value when the user account is first created.
  2. false - at the time the Parse.User object was last refreshed, the user had not confirmed his or her email address. If emailVerified is false, consider calling fetch on the Parse.User.
  3. missing - the Parse.User was created when email verification was off or the Parse.User does not have an email.

Current User

It would be bothersome if the user had to log in every time they open your app. You can avoid this by using the cached current Parse.User object.

Whenever you use any signup or login methods, the user is cached in localStorage. You can treat this cache as a session, and automatically assume the user is logged in:

var currentUser = Parse.User.current();
if (currentUser) {
    // do stuff with the user
} else {
    // show the signup or login page
}

You can clear the current user by logging them out:

Parse.User.logOut();

var currentUser = Parse.User.current();  // this will now be null

Setting the Current User

If you’ve created your own authentication routines, or otherwise logged in a user on the server side, you can now pass the session token to the client and use the become method. This method will ensure the session token is valid before setting the current user.

Parse.User.become("session-token-here").then(function (user) {
  // The current user is now set to user.
}, function (error) {
  // The token could not be validated.
});

Security For User Objects

The Parse.User class is secured by default. Data stored in a Parse.User can only be modified by that user. By default, the data can still be read by any client. Thus, some Parse.User objects are authenticated and can be modified, whereas others are read-only.

Specifically, you are not able to invoke any of the save or delete methods unless the Parse.User was obtained using an authenticated method, like logIn or signUp. This ensures that only the user can alter their own data.

The following illustrates this security policy:

var user = Parse.User.logIn("my_username", "my_password", {
  success: function(user) {
    user.set("username", "my_new_username");  // attempt to change username
    user.save(null, {
      success: function(user) {
        // This succeeds, since the user was authenticated on the device

        // Get the user from a non-authenticated method
        var query = new Parse.Query(Parse.User);
        query.get(user.objectId, {
          success: function(userAgain) {
            userAgain.set("username", "another_username");
            userAgain.save(null, {
              error: function(userAgain, error) {
                // This will error, since the Parse.User is not authenticated
              }
            });
          }
        });
      }
    });
  }
});

The Parse.User obtained from Parse.User.current() will always be authenticated.

If you need to check if a Parse.User is authenticated, you can invoke the authenticated method. You do not need to check authenticated with Parse.User objects that are obtained via an authenticated method.

Security For Other Objects

The same security model that applies to the Parse.User can be applied to other objects. For any object, you can specify which users are allowed to read the object, and which users are allowed to modify an object. To support this type of security, each object has an access control list, implemented by the Parse.ACL class.

The simplest way to use a Parse.ACL is to specify that an object may only be read or written by a single user. This is done by initializing a Parse.ACL with a Parse.User: new Parse.ACL(user) generates a Parse.ACL that limits access to that user. An object's ACL is updated when the object is saved, like any other property. Thus, to create a private note that can only be accessed by the current user:

var Note = Parse.Object.extend("Note");
var privateNote = new Note();
privateNote.set("content", "This note is private!");
privateNote.setACL(new Parse.ACL(Parse.User.current()));
privateNote.save();

This note will then only be accessible to the current user, although it will be accessible to any device where that user is signed in. This functionality is useful for applications where you want to enable access to user data across multiple devices, like a personal todo list.

Permissions can also be granted on a per-user basis. You can add permissions individually to a Parse.ACL using setReadAccess and setWriteAccess. For example, let's say you have a message that will be sent to a group of several users, where each of them have the rights to read and delete that message:

var Message = Parse.Object.extend("Message");
var groupMessage = new Message();
var groupACL = new Parse.ACL();

// userList is an array with the users we are sending this message to.
for (var i = 0; i < userList.length; i++) {
  groupACL.setReadAccess(userList[i], true);
  groupACL.setWriteAccess(userList[i], true);
}

groupMessage.setACL(groupACL);
groupMessage.save();

You can also grant permissions to all users at once using setPublicReadAccess and setPublicWriteAccess. This allows patterns like posting comments on a message board. For example, to create a post that can only be edited by its author, but can be read by anyone:

var publicPost = new Post();
var postACL = new Parse.ACL(Parse.User.current());
postACL.setPublicReadAccess(true);
publicPost.setACL(postACL);
publicPost.save();

Operations that are forbidden, such as deleting an object that you do not have write access to, result in a Parse.Error.OBJECT_NOT_FOUND error code. For security purposes, this prevents clients from distinguishing which object ids exist but are secured, versus which object ids do not exist at all.

Resetting Passwords

It's a fact that as soon as you introduce passwords into a system, users will forget them. In such cases, our library provides a way to let them securely reset their password.

To kick off the password reset flow, ask the user for their email address, and call:

Parse.User.requestPasswordReset("email@example.com", {
  success: function() {
  // Password reset request was sent successfully
  },
  error: function(error) {
    // Show the error message somewhere
    alert("Error: " + error.code + " " + error.message);
  }
});

This will attempt to match the given email with the user's email or username field, and will send them a password reset email. By doing this, you can opt to have users use their email as their username, or you can collect it separately and store it in the email field.

The flow for password reset is as follows:

  1. User requests that their password be reset by typing in their email.
  2. Parse sends an email to their address, with a special password reset link.
  3. User clicks on the reset link, and is directed to a special Parse page that will allow them type in a new password.
  4. User types in a new password. Their password has now been reset to a value they specify.

Note that the messaging in this flow will reference your app by the name that you specified when you created this app on Parse.

Querying

To query for users, you can simple create a new Parse.Query for Parse.Users:

var query = new Parse.Query(Parse.User);
query.equalTo("gender", "female");  // find all the women
query.find({
  success: function(women) {
    // Do stuff
  }
});

Associations

Associations involving a Parse.User work right of the box. For example, let's say you're making a blogging app. To store a new post for a user and retrieve all their posts:

var user = Parse.User.current();

// Make a new post
var Post = Parse.Object.extend("Post");
var post = new Post();
post.set("title", "My New Post");
post.set("body", "This is some great content.");
post.set("user", user);
post.save(null, {
  success: function(post) {
    // Find all posts by the current user
    var query = new Parse.Query(Post);
    query.equalTo("user", user);
    query.find({
      success: function(usersPosts) {
        // userPosts contains all of the posts by the current user.
      }
    });
  }
});

Facebook Users

Parse provides an easy way to integrate Facebook with your application. The Parse.FacebookUtils class integrates Parse.User and the Facebook Javascript SDK to make linking your users to their Facebook identities easy.

Using our Facebook integration, you can associate an authenticated Facebook user with a Parse.User. With just a few lines of code, you'll be able to provide a "log in with Facebook" option in your app, and be able to save their data to Parse.

Setup

To start using Facebook with Parse, you need to:

  1. Set up a Facebook app, if you haven't already. Choose the "Website with Facebook Login" option under "Select how your app integrates with Facebook" and enter your site's URL.
  2. Add your application's Facebook Application ID on your Parse application's settings page.
  3. Follow these instructions for loading the Facebook JavaScript SDK into your application.
  4. Replace your call to FB.init() with a call to Parse.FacebookUtils.init(). For example, if you load the Facebook JavaScript SDK asynchronously, your fbAsyncInit function will look like this:
<script>
  // Initialize Parse
  Parse.initialize("$PARSE_APPLICATION_ID", "$PARSE_JAVASCRIPT_KEY");

      window.fbAsyncInit = function() {
    Parse.FacebookUtils.init({ // this line replaces FB.init({
      appId      : '{facebook-app-id}', // Facebook App ID
      status     : true,  // check Facebook Login status
      cookie     : true,  // enable cookies to allow Parse to access the session
      xfbml      : true,  // initialize Facebook social plugins on the page
      version    : 'v2.3' // point to the latest Facebook Graph API version
    });

        // Run code after the Facebook SDK is loaded.
  };

      (function(d, s, id){
    var js, fjs = d.getElementsByTagName(s)[0];
    if (d.getElementById(id)) {return;}
    js = d.createElement(s); js.id = id;
    js.src = "//connect.facebook.net/en_US/sdk.js";
    fjs.parentNode.insertBefore(js, fjs);
  }(document, 'script', 'facebook-jssdk'));
</script>

The function assigned to fbAsyncInit is run as soon as the Facebook JavaScript SDK has completed loading. Any code that you want to run after the Facebook JavaScript SDK is loaded should be placed within this function and after the call to Parse.FacebookUtils.init().

If you encounter any issues that are Facebook-related, a good resource is the official getting started guide from Facebook.

If you encounter issues that look like they're being returned from Parse's servers, try removing your Facebook application's App Secret from your app's settings page.

There are two main ways to use Facebook with your Parse users: (1) logging in as a Facebook user and creating a Parse.User, or (2) linking Facebook to an existing Parse.User.

Login & Signup

Parse.FacebookUtils provides a way to allow your Parse.Users to log in or sign up through Facebook. This is accomplished using the logIn() method:

Parse.FacebookUtils.logIn(null, {
  success: function(user) {
    if (!user.existed()) {
      alert("User signed up and logged in through Facebook!");
    } else {
      alert("User logged in through Facebook!");
    }
  },
  error: function(user, error) {
    alert("User cancelled the Facebook login or did not fully authorize.");
  }
});

When this code is run, the following happens:

  1. The user is shown the Facebook login dialog.
  2. The user authenticates via Facebook, and your app receives a callback.
  3. Our SDK receives the Facebook data and saves it to a Parse.User. If it's a new user based on the Facebook ID, then that user is created.
  4. Your success callback is called with the user.

You may optionally provide a comma-delimited string that specifies what permissions your app requires from the Facebook user. For example:

Parse.FacebookUtils.logIn("user_likes,email", {
  success: function(user) {
    // Handle successful login
  },
  error: function(user, error) {
    // Handle errors and cancellation
  }
});

Parse.User integration doesn't require any permissions to work out of the box (ie. null or specifying no permissions is perfectly acceptable). Read more about permissions on Facebook's developer guide.

It is up to you to record any data that you need from the Facebook user after they authenticate. To accomplish this, you'll need to do a graph query via Facebook's SDK.

Linking

If you want to associate an existing Parse.User to a Facebook account, you can link it like so:

if (!Parse.FacebookUtils.isLinked(user)) {
  Parse.FacebookUtils.link(user, null, {
    success: function(user) {
      alert("Woohoo, user logged in with Facebook!");
    },
    error: function(user, error) {
      alert("User cancelled the Facebook login or did not fully authorize.");
    }
  });
}

The steps that happen when linking are very similar to log in. The difference is that on successful login, the existing Parse.User is updated with the Facebook information. Future logins via Facebook will now log the user into their existing account.

If you want to unlink Facebook from a user, simply do this:

Parse.FacebookUtils.unlink(user, {
  success: function(user) {
    alert("The user is no longer associated with their Facebook account.");
  }
});

Facebook SDK and Parse

The Facebook Javascript SDK provides a main FB object that is the starting point for many of the interactions with Facebook's API. You can read more about their SDK here.

Facebook login using the Parse SDK requires that the Facebook SDK already be loaded before calling Parse.FacebookUtils.init().

Our library manages the FB object for you. The FB singleton is synchronized with the current user by default, so any methods you call on it will be acting on the Facebook user associated with the current Parse.User. Calling FB.login() or FB.logOut() explicitly will cause the Parse.User and FB object to fall out of synchronization, and is not recommended.

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.

Sessions

Sessions represent an instance of a user logged into a device. Sessions are automatically created when users log in or sign up. They are automatically deleted when users log out. There is one distinct Parse.Session object for each user-installation pair; if a user issues a login request from a device they're already logged into, that user's previous Parse.Session object for that Installation is automatically deleted. Parse.Session objects are stored on Parse in the Session class, and you can view them on the Parse.com Data Browser. We provide a set of APIs to manage Parse.Session objects in your app.

Session APIs are only available in apps with revocable sessions enabled. Parse apps created after March 25, 2015 have this enabled by default ("Require Revocable Sessions" toggle in your Parse.com app settings page). If you have an existing app, you can upgrade to revocable sessions by following the Session Migration Tutorial.

Parse.Session is a subclass of Parse.Object, so you can query, update, and delete sessions in the same way that you manipulate normal objects on Parse. Because the Parse Cloud automatically creates sessions when you log in or sign up users, you should not manually create Parse.Session objects unless you are building a "Parse for IoT" app (e.g. Arduino or Embedded C). Deleting a Parse.Session will log the user out of the device that is currently using this session's token.

Unlike other Parse objects, the Parse.Session class does not have Cloud Code triggers. So you cannot register a beforeSave or afterSave handler for the Session class.

Properties

The Parse.Session object has these special fields:

Handling Invalid Session Token Error

Apps created before March 25, 2015 use legacy session tokens until you migrate them to use the new revocable sessions. On API requests with legacy tokens, if the token is invalid (e.g. User object was deleted), then the request is executed as a non-logged in user and no error was returned. On API requests with revocable session tokens, an invalid session token will always fail with the "invalid session token" error. This new behavior lets you know when you need to ask the user to log in again.

With revocable sessions, your current session token could become invalid if its corresponding Parse.Session object is deleted from the Parse Cloud. This could happen if you implement a Session Manager UI that lets users log out of other devices, or if you manually delete the session via Cloud Code, REST API, or Data Browser. Sessions could also be deleted due to automatic expiration (if configured in app settings). When a device's session token no longer corresponds to a Parse.Session object on the Parse Cloud, all API requests from that device will fail with “Error 209: invalid session token”.

To handle this error, we recommend writing a global utility function that is called by all of your Parse request error callbacks. You can then handle the "invalid session token" error in this global function. You should prompt the user to login again so that they can obtain a new session token. This code could look like this:

@interface ParseErrorHandlingController : NSObject

+ (void)handleParseError:(NSError *)error;

@end

@implementation ParseErrorHandlingController

+ (void)handleError:(NSError *)error {
  if (![error.domain isEqualToString:PFParseErrorDomain]) {
    return;
  }

  switch (error.code) {
    case kPFErrorInvalidSessionToken: {
      [self _handleInvalidSessionTokenError];
      break;
    }
    ... // Other Parse API Errors that you want to explicitly handle.
  }
}

+ (void)_handleInvalidSessionTokenError {
  //--------------------------------------
  // Option 1: Show a message asking the user to log out and log back in.
  //--------------------------------------
  // If the user needs to finish what they were doing, they have the opportunity to do so.
  //
  // UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Invalid Session"
  //                                                     message:@"Session is no longer valid, please log out and log in again."
  //                                                    delegate:self
  //                                           cancelButtonTitle:@"Not Now"
  //                                           otherButtonTitles:@"OK"];
  // [alertView show];

  //--------------------------------------
  // Option #2: Show login screen so user can re-authenticate.
  //--------------------------------------
  // You may want this if the logout button is inaccessible in the UI.
  //
  // UIViewController *presentingViewController = [[UIApplication sharedApplication].keyWindow.rootViewController;
  // PFLogInViewController *logInViewController = [[PFLogInViewController alloc] init];
  // [presentingViewController presentViewController:logInViewController animated:YES completion:nil];
}

@end

// In all API requests, call the global error handler, e.g.
[[PFQuery queryWithClassName:@"Object"] findInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
  if (!error) {
    // Query succeeded - continue your app logic here.
  } else {
    // Query failed - handle an error.
    [ParseErrorHandlingController handleParseError:error];
  }
}];
class ParseErrorHandlingController {
  class func handleParseError(error: NSError) {
    if error.domain != PFParseErrorDomain {
      return
    }

    switch (error.code) {
    case kPFErrorInvalidSessionToken:
      handleInvalidSessionTokenError()

    ... // Other Parse API Errors that you want to explicitly handle.
  }

  private class func handleInvalidSessionTokenError() {
    //--------------------------------------
    // Option 1: Show a message asking the user to log out and log back in.
    //--------------------------------------
    // If the user needs to finish what they were doing, they have the opportunity to do so.
    //
    // let alertView = UIAlertView(
    //   title: "Invalid Session",
    //   message: "Session is no longer valid, please log out and log in again.",
    //   delegate: nil,
    //   cancelButtonTitle: "Not Now",
    //   otherButtonTitles: "OK"
    // )
    // alertView.show()

    //--------------------------------------
    // Option #2: Show login screen so user can re-authenticate.
    //--------------------------------------
    // You may want this if the logout button is inaccessible in the UI.
    //
    // let presentingViewController = UIApplication.sharedApplication().keyWindow?.rootViewController
    // let logInViewController = PFLogInViewController()
    // presentingViewController?.presentViewController(logInViewController, animated: true, completion: nil)
  }
}

// In all API requests, call the global error handler, e.g.
let query = PFQuery(className: "Object")
query.findObjectsInBackgroundWithBlock { (objects: [AnyObject]!, error: NSError!) -> Void in
  if error == nil {
    // Query Succeeded - continue your app logic here.
  } else {
    // Query Failed - handle an error.
    ParseErrorHandlingController.handleParseError(error)
  }
}
public class ParseErrorHandler {
  public static void handleParseError(ParseException e) {
    switch (e.getCode()) {
      case INVALID_SESSION_TOKEN: handleInvalidSessionToken()
        break;

      ... // Other Parse API errors that you want to explicitly handle
    }
  }

  private static void handleInvalidSessionToken() {
    //--------------------------------------
    // Option 1: Show a message asking the user to log out and log back in.
    //--------------------------------------
    // If the user needs to finish what they were doing, they have the opportunity to do so.
    //
    // new AlertDialog.Builder(getActivity())
    //   .setMessage("Session is no longer valid, please log out and log in again.")
    //   .setCancelable(false).setPositiveButton("OK", ...).create().show();

    //--------------------------------------
    // Option #2: Show login screen so user can re-authenticate.
    //--------------------------------------
    // You may want this if the logout button could be inaccessible in the UI.
    //
    // startActivityForResult(new ParseLoginBuilder(getActivity()).build(), 0);
  }
});

// In all API requests, call the global error handler, e.g.
query.findInBackground(new FindCallback<ParseObject>() {
  public void done(List<ParseObject> results, ParseException e) {
    if (e == null) {
      // Query successful, continue other app logic
    } else {
      // Query failed
      ParseErrorHandler.handleParseError(e);
    }
  }
});
function handleParseError(err) {
  switch (err.code) {
    case Parse.Error.INVALID_SESSION_TOKEN:
      Parse.User.logOut();
      ... // If web browser, render a log in screen
      ... // If Express.js, redirect the user to the log in route
      break;

    ... // Other Parse API errors that you want to explicitly handle
  }
}

// For each API request, call the global error handler
query.find().then(function() {
  ...
}, function(err) {
  handleParseError(err);
});
public class ParseErrorHandler {
  public static void HandleParseError(ParseException e) {
    switch (e.Code) {
      case ParseException.ErrorCode.InvalidSessionToken:
        HandleInvalidSessionToken()
        break;

      ... // Other Parse API errors that you want to explicitly handle
    }
  }

  private static void HandleInvalidSessionToken() {
    //--------------------------------------
    // Option 1: Show a message asking the user to log out and log back in.
    //--------------------------------------
    // If the user needs to finish what they were doing, they have the opportunity to do so.

    //--------------------------------------
    // Option #2: Show login screen so user can re-authenticate.
    //--------------------------------------
    // You may want this if the logout button is inaccessible in the UI.
  }
});

// In all API requests, call the global error handler, e.g.
query.FindAsync().ContinueWith(t => {
  if (t.IsFaulted) {
    // Query Failed - handle an error.
    ParseErrorHandler.HandleParseError(t.Exception.InnerException as ParseException);
  } else {
    // Query Succeeded - continue your app logic here.
  }
});
public class ParseErrorHandler {
  public static handleParseError(ParseException $e) {
    $code = $e->getCode();
    switch ($code) {
      case: 209: // INVALID_SESSION_TOKEN
        ParseUser::logOut();
        ... // Redirect the to login page.
        break;

      ... // Other Parse API errors that you want to explicitly handle
    }
  }
});

// For each API request, call the global error handler
try {
  $results = $query->find();
  // ...
} catch (ParseException $e) {
  ParseErrorHandler::handleParseError($e)
}

Security

Parse.Session objects can only be accessed by the user specified in the user field. All Parse.Session objects have an ACL that is read and write by that user only. You cannot change this ACL. This means querying for sessions will only return objects that match the current logged-in user.

When you log in a user via Parse.User.login(), Parse will automatically create a new unrestricted Parse.Session object in the Parse Cloud. Same for signups and Facebook/Twitter logins.

Session objects manually created from client SDKs (by creating an instance of Parse.Session, and saving it) are always restricted. You cannot manually create an unrestricted sessions using the object creation API.

Restricted sessions are prohibited from creating, modifying, or deleting any data in the Parse.User, Parse.Session, and Parse.Role classes. Restricted session also cannot read unrestricted sessions. Restricted Sessions are useful for "Parse for IoT" devices (e.g Arduino or Embedded C) that may run in a less-trusted physical environment than mobile apps. However, please keep in mind that restricted sessions can still read data on Parse.User, Parse.Session, and Parse.Role classes, and can read/write data in any other class just like a normal session. So it is still important for IoT devices to be in a safe physical environment and ideally use encrypted storage to store the session token.

If you want to prevent restricted Sessions from modifying classes other than Parse.User, Parse.Session, or Parse.Role, you can write a Cloud Code beforeSave handler for that class:

Parse.Cloud.beforeSave("MyClass", function(request, response) {
  Parse.Session.current().then(function(session) {
    if (session.get('restricted')) {
      response.error('write operation not allowed');
    }
    response.success();
  });
});

You can configure Class-Level Permissions (CLPs) for the Session class just like other classes on Parse. CLPs restrict reading/writing of sessions via the Parse.Session API, but do not restrict Parse Cloud's automatic session creation/deletion when users log in, sign up, and log out. We recommend that you disable all CLPs not needed by your app. Here are some common use cases for Session CLPs:

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.

Roles

As your app grows in scope and user-base, you may find yourself needing more coarse-grained control over access to pieces of your data than user-linked ACLs can provide. To address this requirement, Parse supports a form of Role-based Access Control. Roles provide a logical way of grouping users with common access privileges to your Parse data. Roles are named objects that contain users and other roles. Any permission granted to a role is implicitly granted to its users as well as to the users of any roles that it contains.

For example, in your application with curated content, you may have a number of users that are considered "Moderators" and can modify and delete content created by other users. You may also have a set of users that are "Administrators" and are allowed all of the same privileges as Moderators, but can also modify the global settings for the application. By adding users to these roles, you can ensure that new users can be made moderators or administrators, without having to manually grant permission to every resource for each user.

We provide a specialized class called Parse.Role that represents these role objects in your client code. Parse.Role is a subclass of Parse.Object, and has all of the same features, such as a flexible schema, automatic persistence, and a key value interface. All the methods that are on Parse.Object also exist on Parse.Role. The difference is that Parse.Role has some additions specific to management of roles.

Properties

Parse.Role has several properties that set it apart from Parse.Object:

Security for Role Objects

The Parse.Role uses the same security scheme (ACLs) as all other objects on Parse, except that it requires an ACL to be set explicitly. Generally, only users with greatly elevated privileges (e.g. a master user or Administrator) should be able to create or modify a Role, so you should define its ACLs accordingly. Remember, if you give write-access to a Parse.Role to a user, that user can add other users to the role, or even delete the role altogether.

To create a new Parse.Role, you would write:

// By specifying no write privileges for the ACL, we can ensure the role cannot be altered.
var roleACL = new Parse.ACL();
roleACL.setPublicReadAccess(true);
var role = new Parse.Role("Administrator", roleACL);
role.save();

You can add users and roles that should inherit your new role's permissions through the "users" and "roles" relations on Parse.Role:

var role = new Parse.Role(roleName, roleACL);
role.getUsers().add(usersToAddToRole);
role.getRoles().add(rolesToAddToRole);
role.save();

Take great care when assigning ACLs to your roles so that they can only be modified by those who should have permissions to modify them.

Security for Other Objects

Now that you have created a set of roles for use in your application, you can use them with ACLs to define the privileges that their users will receive. Each Parse.Object can specify a Parse.ACL, which provides an access control list that indicates which users and roles should be granted read or write access to the object.

Giving a role read or write permission to an object is straightforward. You can either use the Parse.Role:

var moderators = /* Query for some Parse.Role */;
var wallPost = new Parse.Object("WallPost");
var postACL = new Parse.ACL();
postACL.setRoleWriteAccess(moderators, true);
wallPost.setACL(postACL);
wallPost.save();

You can avoid querying for a role by specifying its name for the ACL:

var wallPost = new Parse.Object("WallPost");
var postACL = new Parse.ACL();
postACL.setRoleWriteAccess("Moderators", true);
wallPost.setACL(postACL);
wallPost.save();

Role Hierarchy

As described above, one role can contain another, establishing a parent-child relationship between the two roles. The consequence of this relationship is that any permission granted to the parent role is implicitly granted to all of its child roles.

These types of relationships are commonly found in applications with user-managed content, such as forums. Some small subset of users are "Administrators", with the highest level of access to tweaking the application's settings, creating new forums, setting global messages, and so on. Another set of users are "Moderators", who are responsible for ensuring that the content created by users remains appropriate. Any user with Administrator privileges should also be granted the permissions of any Moderator. To establish this relationship, you would make your "Administrators" role a child role of "Moderators", like this:

var administrators = /* Your "Administrators" role */;
var moderators = /* Your "Moderators" role */;
moderators.getRoles().add(administrators);
moderators.save();
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.

Files

Creating a Parse.File

Parse.File lets you store application files in the cloud that would otherwise be too large or cumbersome to fit into a regular Parse.Object. The most common use case is storing images, but you can also use it for documents, videos, music, and any other binary data (up to 10 megabytes).

Getting started with Parse.File is easy. There are a couple of ways to create a file. The first is with a base64-encoded String.

var base64 = "V29ya2luZyBhdCBQYXJzZSBpcyBncmVhdCE=";
var file = new Parse.File("myfile.txt", { base64: base64 });

Alternatively, you can create a file from an array of byte values:

var bytes = [ 0xBE, 0xEF, 0xCA, 0xFE ];
var file = new Parse.File("myfile.txt", bytes);

Parse will auto-detect the type of file you are uploading based on the file extension, but you can specify the Content-Type with a third parameter:

var file = new Parse.File("myfile.zzz", fileData, "image/png");

But most commonly for HTML5 apps, you'll want to use an html form with a file upload control. On modern browsers, this is easy. Create a file input tag which allows the user to pick a file from their local drive to upload:

    <input type="file" id="profilePhotoFileUpload">

Then, in a click handler or other function, get a reference to that file:

var fileUploadControl = $("#profilePhotoFileUpload")[0];
if (fileUploadControl.files.length > 0) {
  var file = fileUploadControl.files[0];
  var name = "photo.jpg";

  var parseFile = new Parse.File(name, file);
}

Notice in this example that we give the file a name of photo.jpg. There's two things to note here:

Next you'll want to save the file up to the cloud. As with Parse.Object, there are many variants of the save method you can use depending on what sort of callback and error handling suits you.

parseFile.save().then(function() {
  // The file has been saved to Parse.
}, function(error) {
  // The file either could not be read, or could not be saved to Parse.
});

Finally, after the save completes, you can associate a Parse.File with a Parse.Object just like any other piece of data:

var jobApplication = new Parse.Object("JobApplication");
jobApplication.set("applicantName", "Joe Smith");
jobApplication.set("applicantResumeFile", parseFile);
jobApplication.save();

Retrieving File Contents

How to best retrieve the file contents back depends on the context of your application. Because of cross-domain request issues, it's best if you can make the browser do the work for you. Typically, that means rendering the file's URL into the DOM. Here we render an uploaded profile photo on a page with jQuery:

var profilePhoto = profile.get("photoFile");
$("profileImg")[0].src = profilePhoto.url();

If you want to process a File's data in Cloud Code, you can retrieve the file using our http networking libraries:

Parse.Cloud.httpRequest({ url: profilePhoto.url() }).then(function(response) {
  // The file contents are in response.buffer.
});

You can delete files that are referenced by objects using the REST API. You will need to provide the master key in order to be allowed to delete a file.

If your files are not referenced by any object in your app, it is not possible to delete them through the REST API. You may request a cleanup of unused files in your app's Settings page. Keep in mind that doing so may break functionality which depended on accessing unreferenced files through their URL property. Files that are currently associated with an object will not be affected.

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.

Promises

In addition to callbacks, every asynchronous method in the Parse JavaScript SDK returns a Promise. With promises, your code can be much cleaner than the nested code you get with callbacks.

Introduction to Promises

Promises represent the next great paradigm in JavaScript programming. But understanding why they are so great is no simple matter. At its core, a Promise represents the result of a task, which may or may not have completed. The only interface requirement of a Promise is having a function called then, which can be given callbacks to be called when the promise is fulfilled or has failed. This is outlined in the CommonJS Promises/A proposal. For example, consider saving a Parse.Object, which is an asynchronous operation. In the old callback paradigm, your code would look like this:

object.save({ key: value }, {
  success: function(object) {
    // the object was saved.
  },
  error: function(object, error) {
    // saving the object failed.
  }
});

In the new Promise paradigm, that same code would look like this:

object.save({ key: value }).then(
  function(object) {
    // the object was saved.
  },
  function(error) {
    // saving the object failed.
  });

Not much different, right? So what’s the big deal? Well, the real power of promises comes from chaining multiple of them together. Calling promise.then(func) returns a new promise, which is not fulfilled until func has completed. But there’s one really special thing about the way func is used. If a callback supplied to then returns a new promise, then the promise returned by then will not be fulfilled until the promise returned by the callback is fulfilled. The details of the behavior are explained in the Promises/A+ proposal. This is a complex topic, but maybe an example would make it clearer.

Imagine you’re writing code to log in, find an object, and then update it. In the old callback paradigm, you’d end up with what we call pyramid code:

Parse.User.logIn("user", "pass", {
  success: function(user) {
    query.find({
      success: function(results) {
        results[0].save({ key: value }, {
          success: function(result) {
            // the object was saved.
          }
        });
      }
    });
  }
});

That’s getting pretty ridiculous, and that’s without any error handling code even. But because of the way promise chaining works, the code can now be much flatter:

Parse.User.logIn("user", "pass").then(function(user) {
  return query.find();
}).then(function(results) {
  return results[0].save({ key: value });
}).then(function(result) {
  // the object was saved.
});

Ah! Much better!

The then Method

Every Promise has a method named then which takes a pair of callbacks. The first callback is called if the promise is resolved, while the second is called if the promise is rejected.

obj.save().then(function(obj) {
  // the object was saved successfully.
}, function(error) {
  // the save failed.
});

Chaining Promises Together

Promises are a little bit magical, in that they let you chain them without nesting. If a callback for a promise returns a new promise, then the first one will not be resolved until the second one is. This lets you perform multiple actions without incurring the pyramid code you would get with callbacks.

var query = new Parse.Query("Student");
query.descending("gpa");
query.find().then(function(students) {
  students[0].set("valedictorian", true);
  return students[0].save();

}).then(function(valedictorian) {
  return query.find();

}).then(function(students) {
  students[1].set("salutatorian", true);
  return students[1].save();

}).then(function(salutatorian) {
  // Everything is done!

});

Error Handling

The code samples above left out error handling for simplicity, but adding it back reiterates what a mess the old callback code could be:

Parse.User.logIn("user", "pass", {
  success: function(user) {
    query.find({
      success: function(results) {
        results[0].save({ key: value }, {
          success: function(result) {
            // the object was saved.
          },
          error: function(result, error) {
            // An error occurred.
          }
        });
      },
      error: function(error) {
        // An error occurred.
      }
    });
  },
  error: function(user, error) {
    // An error occurred.
  }
});

Because promises know whether they’ve been fulfilled or failed, they can propagate errors, not calling any callback until an error handler is encountered. For example, the code above could be written simply as:

Parse.User.logIn("user", "pass").then(function(user) {
  return query.find();
}).then(function(results) {
  return results[0].save({ key: value });
}).then(function(result) {
  // the object was saved.
}, function(error) {
  // there was some error.
});

Generally, developers consider a failing promise to be the asynchronous equivalent to throwing an exception. In fact, if a callback passed to then throws an error, the promise returned will fail with that error. If any Promise in a chain returns an error, all of the success callbacks after it will be skipped until an error callback is encountered. The error callback can transform the error, or it can handle it by returning a new Promise that isn't rejected. You can think of rejected promises kind of like throwing an exception. An error callback is like a catch block that can handle the error or rethrow it.

var query = new Parse.Query("Student");
query.descending("gpa");
query.find().then(function(students) {
  students[0].set("valedictorian", true);
  // Force this callback to fail.
  return Parse.Promise.error("There was an error.");

}).then(function(valedictorian) {
  // Now this will be skipped.
  return query.find();

}).then(function(students) {
  // This will also be skipped.
  students[1].set("salutatorian", true);
  return students[1].save();
}, function(error) {
  // This error handler WILL be called. error will be "There was an error.".
  // Let's handle the error by returning a new promise.
  return Parse.Promise.as("Hello!");

}).then(function(hello) {
  // Everything is done!
}, function(error) {
  // This isn't called because the error was already handled.
});

It's often convenient to have a long chain of success callbacks with only one error handler at the end.

Creating Promises

When you're getting started, you can just use the promises returned from methods like find or save. However, for more advanced scenarios, you may want to make your own promises. After you create a Promise, you'll need to call resolve or reject to trigger its callbacks.

var successful = new Parse.Promise();
successful.resolve("The good result.");

var failed = new Parse.Promise();
failed.reject("An error message.");

If you know the result of a promise at the time it is created, there are some convenience methods you can use.

var successful = Parse.Promise.as("The good result.");

var failed = Parse.Promise.error("An error message.");

Promises in Series

Promises are convenient when you want to do a series of tasks in a row, each one waiting for the previous to finish. For example, imagine you want to delete all of the comments on your blog.

var query = new Parse.Query("Comments");
query.equalTo("post", 123);

query.find().then(function(results) {
  // Create a trivial resolved promise as a base case.
  var promise = Parse.Promise.as();
  _.each(results, function(result) {
    // For each item, extend the promise with a function to delete it.
    promise = promise.then(function() {
      // Return a promise that will be resolved when the delete is finished.
      return result.destroy();
    });
  });
  return promise;

}).then(function() {
  // Every comment was deleted.
});

Promises in Parallel

You can also use promises to perform several tasks in parallel, using the when method. You can start multiple operations at once, and use Parse.Promise.when to create a new promise that will be resolved when all of its input promises is resolved. The new promise will be successful if none of the passed-in promises fail; otherwise, it will fail with the last error. Performing operations in parallel will be faster than doing them serially, but may consume more system resources and bandwidth.

var query = new Parse.Query("Comments");
query.equalTo("post", 123);

query.find().then(function(results) {
  // Collect one promise for each delete into an array.
  var promises = [];
  _.each(results, function(result) {
    // Start this delete immediately and add its promise to the list.
    promises.push(result.destroy());
  });
  // Return a new promise that is resolved when all of the deletes are finished.
  return Parse.Promise.when(promises);

}).then(function() {
  // Every comment was deleted.
});

Creating Async Methods

With these tools, it's easy to make your own asynchronous functions that return promises. For example, you can make a promisified version of setTimeout.

var delay = function(millis) {
  var promise = new Parse.Promise();
  setTimeout(function() {
    promise.resolve();
  }, millis);
  return promise;
};

delay(100).then(function() {
  // This ran after 100ms!
});
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.

GeoPoints

Parse allows you to associate real-world latitude and longitude coordinates with an object. Adding a Parse.GeoPoint to a Parse.Object allows queries to take into account the proximity of an object to a reference point. This allows you to easily do things like find out what user is closest to another user or which places are closest to a user.

Parse.GeoPoint

To associate a point with an object you first need to create a Parse.GeoPoint. For example, to create a point with latitude of 40.0 degrees and -30.0 degrees longitude:

var point = new Parse.GeoPoint({latitude: 40.0, longitude: -30.0});

This point is then stored in the object as a regular field.

placeObject.set("location", point);

Note: Currently only one key in a class may be a Parse.GeoPoint.

Geo Queries

Now that you have a bunch of objects with spatial coordinates, it would be nice to find out which objects are closest to a point. This can be done by adding another restriction to Parse.Query using near. Getting a list of ten places that are closest to a user may look something like:

// User's location
var userGeoPoint = userObject.get("location");
// Create a query for places
var query = new Parse.Query(PlaceObject);
// Interested in locations near user.
query.near("location", userGeoPoint);
// Limit what could be a lot of points.
query.limit(10);
// Final list of objects
query.find({
  success: function(placesObjects) {
  }
});

At this point placesObjects will be an array of objects ordered by distance (nearest to farthest) from userGeoPoint. Note that if an additional ascending()/descending() order-by constraint is applied, it will take precedence over the distance ordering.

To limit the results using distance, check out withinMiles, withinKilometers, and withinRadians.

It's also possible to query for the set of objects that are contained within a particular area. To find the objects in a rectangular bounding box, add the withinGeoBox restriction to your Parse.Query.

var southwestOfSF = new Parse.GeoPoint(37.708813, -122.526398);
var northeastOfSF = new Parse.GeoPoint(37.822802, -122.373962);

var query = new Parse.Query(PizzaPlaceObject);
query.withinGeoBox("location", southwestOfSF, northeastOfSF);
query.find({
  success: function(pizzaPlacesInSF) {
    ...
  }
});

Caveats

At the moment there are a couple of things to watch out for:

  1. Each Parse.Object class may only have one key with a Parse.GeoPoint object.
  2. Using the near constraint will also limit results to within 100 miles.
  3. Points should not equal or exceed the extreme ends of the ranges. Latitude should not be -90.0 or 90.0. Longitude should not be -180.0 or 180.0. Attempting to set latitude or longitude out of bounds will cause an error.
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.

Relations

There are three kinds of relationships. One-to-one relationships enable one object to be associated with another object. One-to-many relationships enable one object to have many related objects. Finally, many-to-many relationships enable complex relationships among many objects.

There are four ways to build relationships in Parse:

One-to-Many

When you’re thinking about one-to-many relationships and whether to implement Pointers or Arrays, there are several factors to consider. First, how many objects are involved in this relationship? If the "many" side of the relationship could contain a very large number (greater than 100 or so) of objects, then you have to use Pointers. If the number of objects is small (fewer than 100 or so), then Arrays may be more convenient, especially if you typically need to get all of the related objects (the "many" in the "one-to-many relationship") at the same time as the parent object.

Using Pointers

Let's say we have a game app. The game keeps track of the player's score and achievements every time she chooses to play. In Parse, we can store this data in a single Game object. If the game becomes incredibly successful, each player will store thousands of Game objects in the system. For circumstances like this, where the number of relationships can be arbitrarily large, Pointers are the best option.

Suppose in this game app, we want to make sure that every Game object is associated with a Parse User. We can implement this like so:

ParseObject game = new ParseObject("Game");
game.put("createdBy", ParseUser.getCurrentUser());
PFObject *game= [PFObject objectWithClassName:@"Game"];
[game setObject:[PFUser currentUser] forKey:@"createdBy"];
let game = PFObject(className:"Game")
game["createdBy"] = PFUser.currentUser()
$game = ParseObject::create("Game");
$game->set("createdBy", ParseUser::getCurrentUser());
var game = new ParseObject("Game");
game["createdBy"] = ParseUser.CurrentUser;
var game = new Parse.Object("Game");
game.set("createdBy", Parse.User.current());

We can obtain all of the Game objects created by a Parse User with a query:

ParseQuery<ParseObject> gameQuery = ParseQuery.getQuery("Game");
gameQuery.whereEqualTo("createdBy", ParseUser.getCurrentUser());
PFQuery *gameQuery = [PFQuery queryWithClassName:@"Game"];
[gameQuery whereKey:@"createdBy" equalTo:[PFUser currentUser]];
let gameQuery = PFQuery(className:"Game")
if let user = PFUser.currentUser() {
  gameQuery.whereKey("createdBy", equalTo: user)
}
$gameQuery = new ParseQuery("Game");
$gameQuery->equalTo("createdBy", ParseUser::getCurrentUser());
var query = ParseObject.getQuery("Game").WhereEqualTo("createdBy", ParseUser.CurrentUser);
var query = new Parse.Query("Game");
query.equalTo("createdBy", Parse.User.current());

And, if we want to find the Parse User who created a specific Game, that is a lookup on the createdBy key:

// say we have a Game object
ParseObject game = ...

// getting the user who created the Game
ParseUser createdBy = game.getUser("createdBy");
// say we have a Game object
PFObject *game = ...

// getting the user who created the Game
PFUser *createdBy = [game objectForKey@"createdBy"];
// say we have a Game object
let game = ...

// getting the user who created the Game
let createdBy = game["createdBy"]
// say we have a Game object
$game = ...

// getting the user who created the Game
$user = $game["createdBy"];
// say we have a Game object
ParseObject game = ...

// getting the user who created the Game
ParseUser user = game["createdBy"];
// say we have a Game object
var game = ...

// getting the user who created the Game
var user = game.get("createdBy");

For most scenarios, Pointers will be your best bet for implementing one-to-many relationships.

Using Arrays

Arrays are ideal when we know that the number of objects involved in our one-to-many relationship are going to be small. Arrays will also provide some productivity benefit via the includeKey parameter. Supplying the parameter will enable you to obtain all of the "many" objects in the "one-to-many" relationship at the same time that you obtain the "one" object. However, the response time will be slower if the number of objects involved in the relationship turns out to be large.

Suppose in our game, we enabled players to keep track of all the weapons their character has accumulated as they play, and there can only be a dozen or so weapons. In this example, we know that the number of weapons is not going to be very large. We also want to enable the player to specify the order in which the weapons will appear on screen. Arrays are ideal here because the size of the array is going to be small and because we also want to preserve the order the user has set each time they play the game:

Let's start by creating a column on our Parse User object called weaponsList.

Now let's store some Weapon objects in the weaponsList:

// let's say we have four weapons
ParseObject scimitar = ...
ParseObject plasmaRifle = ...
ParseObject grenade = ...
ParseObject bunnyRabbit = ...

// stick the objects in an array
ArrayList<ParseObject> weapons = new ArrayList<ParseObject>();
weapons.add(scimitar);
weapons.add(plasmaRifle);
weapons.add(grenade);
weapons.add(bunnyRabbit);

// store the weapons for the user
ParseUser.getCurrentUser().put("weaponsList", weapons);
// let's say we have four weapons
PFObject *scimitar = ...
PFObject *plasmaRifle = ...
PFObject *grenade = ...
PFObject *bunnyRabbit = ...

// stick the objects in an array
NSArray *weapons = @[scimitar, plasmaRifle, grenade, bunnyRabbit];

// store the weapons for the user
[[PFUser currentUser] setObject:weapons forKey:@weaponsList"];
// let's say we have four weapons
let scimitar = ...
let plasmaRifle = ...
let grenade = ...
let bunnyRabbit = ...

// stick the objects in an array
let weapons = [scimitar, plasmaRifle, grenade, bunnyRabbit]

// store the weapons for the user
let user = PFUser.currentUser()
user["weaponsList"] = weapons
// let's say we have four weapons
$scimitar = ...
$plasmaRifle = ...
$grenade = ...
$bunnyRabbit = ...

// stick the objects in an array
$weapons = [$scimitar, $plasmaRifle, $grenade, $bunnyRabbit];

// store the weapons for the user
$user = ParseUser::getCurrentUser();
$user->set("weaponsList", weapons);
// no c# example
// no js example

Later, if we want to retrieve the Weapon objects, it's just one line of code:

ArrayList<ParseObject> weapons = ParseUser.getCurrentUser().get("weaponsList");
NSArray *weapons = [[PFUser currentUser] objectForKey:@"weaponsList"];
let weapons = PFUser.currentUser()?.objectForKey("weaponsList")
// no php example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs
// no c# example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs
// no js example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs

Sometimes, we will want to fetch the "many" objects in our one-to-many relationship at the same time as we fetch the "one" object. One trick we could employ is to use the includeKey (or include in Android) parameter whenever we use a Parse Query to also fetch the array of Weapon objects (stored in the weaponsList column) along with the Parse User object:

// set up our query for a User object
ParseQuery<ParseUser> userQuery = ParseUser.getQuery();

// configure any constraints on your query...
// for example, you may want users who are also playing with or against you
// tell the query to fetch all of the Weapon objects along with the user
// get the "many" at the same time that you're getting the "one"
userQuery.include("weaponsList");

// execute the query
userQuery.findInBackground(new FindCallback<ParseUser>() {
  public void done(List<ParseUser> userList, ParseException e) {
    // userList contains all of the User objects, and their associated Weapon objects, too
  }
});
// set up our query for a User object
PFQuery *userQuery = [PFUser query];

// configure any constraints on your query...
// for example, you may want users who are also playing with or against you

// tell the query to fetch all of the Weapon objects along with the user
// get the "many" at the same time that you're getting the "one"
[userQuery includeKey:@"weaponsList"];

// execute the query
[userQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
    // objects contains all of the User objects, and their associated Weapon objects, too
}];
// set up our query for a User object
let userQuery = PFUser.query();

// configure any constraints on your query...
// for example, you may want users who are also playing with or against you

// tell the query to fetch all of the Weapon objects along with the user
// get the "many" at the same time that you're getting the "one"
userQuery?.includeKey("weaponsList");

// execute the query
userQuery?.findObjectsInBackgroundWithBlock {
    (objects: [AnyObject]?, error: NSError?) -> Void in
    // objects contains all of the User objects, and their associated Weapon objects, too
}
// no php example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs
// no c# example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs
// no js example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs

You can also get the "one" side of the one-to-many relationship from the "many" side. For example, if we want to find all Parse User objects who also have a given Weapon, we can write a constraint for our query like this:

// add a constraint to query for whenever a specific Weapon is in an array
userQuery.whereEqualTo("weaponsList", scimitar);

// or query using an array of Weapon objects...
userQuery.whereEqualTo("weaponsList", arrayOfWeapons);
// add a constraint to query for whenever a specific Weapon is in an array
[userQuery whereKey:@"weaponsList" equalTo:scimitar];

// or query using an array of Weapon objects...
[userQuery whereKey:@"weaponsList" containedIn:arrayOfWeapons];
// add a constraint to query for whenever a specific Weapon is in an array
userQuery?.whereKey("weaponsList", equalTo: scimitar);

// or query using an array of Weapon objects...
userQuery?.whereKey("weaponsList", containedIn: arrayOfWeapons)
// no php example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs
// no c# example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs
// no js example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs

Many-to-Many

Now let’s tackle many-to-many relationships. Suppose we had a book reading app and we wanted to model Book objects and Author objects. As we know, a given author can write many books, and a given book can have multiple authors. This is a many-to-many relationship scenario where you have to choose between Arrays, Parse Relations, or creating your own Join Table.

The decision point here is whether you want to attach any metadata to the relationship between two entities. If you don’t, Parse Relation or using Arrays are going to be the easiest alternatives. In general, using arrays will lead to higher performance and require fewer queries. If either side of the many-to-many relationship could lead to an array with more than 100 or so objects, then, for the same reason Pointers were better for one-to-many relationships, Parse Relation or Join Tables will be better alternatives.

On the other hand, if you want to attach metadata to the relationship, then create a separate table (the "Join Table") to house both ends of the relationship. Remember, this is information about the relationship, not about the objects on either side of the relationship. Some examples of metadata you may be interested in, which would necessitate a Join Table approach, include:

Using Parse Relations

Using Parse Relations, we can create a relationship between a Book and a few Author objects. In the Data Browser, you can create a column on the Book object of type relation and name it authors.

After that, we can associate a few authors with this book:

// let’s say we have a few objects representing Author objects
ParseObject authorOne =
ParseObject authorTwo =
ParseObject authorThree =

// now we create a book object
ParseObject book = new ParseObject("Book");

// now let’s associate the authors with the book
// remember, we created a "authors" relation on Book
ParseRelation<ParseObject> relation = book.getRelation("authors");
relation.add(authorOne);
relation.add(authorTwo);
relation.add(authorThree);

// now save the book object
book.saveInBackground();
// let’s say we have a few objects representing Author objects
PFObject *authorOne = …
PFObject *authorTwo = …
PFObject *authorThree = …

// now we create a book object
PFObject *book= [PFObject objectWithClassName:@"Book"];

// now let’s associate the authors with the book
// remember, we created a "authors" relation on Book
PFRelation *relation = [book relationForKey:@"authors"];
[relation addObject:authorOne];
[relation addObject:authorTwo];
[relation addObject:authorThree];

// now save the book object
[book saveInBackground];
// let’s say we have a few objects representing Author objects
let authorOne = ...
let authorTwo = ...
let authorThree = ...

// now we create a book object
let book = PFObject(className: "Book")

// now let’s associate the authors with the book
// remember, we created a "authors" relation on Book
let relation = book.relationForKey("authors")
relation.addObject(authorOne)
relation.addObject(authorTwo)
relation.addObject(authorThree)

// now save the book object
book.saveInBackground()
// no php example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs
// no c# example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs
// no js example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs

To get the list of authors who wrote a book, create a query:

// suppose we have a book object
ParseObject book = ...

// create a relation based on the authors key
ParseRelation relation = book.getRelation("authors");

// generate a query based on that relation
ParseQuery query = relation.getQuery();

// now execute the query
// suppose we have a book object
PFObject *book = ...

// create a relation based on the authors key
PFRelation *relation = [book relationForKey:@"authors"];

// generate a query based on that relation
PFQuery *query = [relation query];

// now execute the query
// suppose we have a book object
let book = ...

// create a relation based on the authors key
let relation = book.relationForKey("authors")

// generate a query based on that relation
let query = relation.query()

// now execute the query
// no php example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs
// no c# example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs
// no js example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs

Perhaps you even want to get a list of all the books to which an author contributed. You can create a slightly different kind of query to get the inverse of the relationship:

// suppose we have a author object, for which we want to get all books
ParseObject author = ...

// first we will create a query on the Book object
ParseQuery<ParseObject> query = ParseQuery.getQuery("Book");

// now we will query the authors relation to see if the author object we have
// is contained therein
query.whereEqualTo("authors", author);
// suppose we have a author object, for which we want to get all books
PFObject *author = ...

// first we will create a query on the Book object
PFQuery *query = [PFQuery queryWithClassName:@"Book"];

// now we will query the authors relation to see if the author object
// we have is contained therein
[query whereKey:@"authors" equalTo:author];
// suppose we have a author object, for which we want to get all books
let author = ...

// first we will create a query on the Book object
let query = PFQuery(className: "Book")

// now we will query the authors relation to see if the author object
// we have is contained therein
query?.whereKey("authors", equalTo: author)
// no php example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs
// no c# example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs
// no js example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs

Using Join Tables

There may be certain cases where we want to know more about a relationship. For example, suppose we were modeling a following/follower relationship between users: a given user can follow another user, much as they would in popular social networks. In our app, we not only want to know if User A is following User B, but we also want to know when User A started following User B. This information could not be contained in a Parse Relation. In order to keep track of this data, you must create a separate table in which the relationship is tracked. This table, which we will call Follow, would have a from column and a to column, each with a pointer to a Parse User. Alongside the relationship, you can also add a column with a Date object named date.

Now, when you want to save the following relationship between two users, create a row in the Follow table, filling in the from, to, and date keys appropriately:

// suppose we have a user we want to follow
ParseUser otherUser = ...

// create an entry in the Follow table
ParseObject follow = new ParseObject("Follow");
follow.put("from", ParseUser.getCurrentUser());
follow.put("to", otherUser);
follow.put("date", Date());
follow.saveInBackground();
// suppose we have a user we want to follow
PFUser *otherUser = ...

// create an entry in the Follow table
PFObject *follow = [PFObject objectWithClassName:@"Follow"];
[follow setObject:[PFUser currentUser]  forKey:@"from"];
[follow setObject:otherUser forKey:@"to"];
[follow setObject:[NSDate date] forKey@"date"];
[follow saveInBackground];
// suppose we have a user we want to follow
let otherUser = ...

// create an entry in the Follow table
let follow = PFObject(className: "Follow")
follow.setObject(PFUser.currentUser()!, forKey: "from")
follow.setObject(otherUser, forKey: "to")
follow.setObject(NSDate(), forKey: "date")
follow.saveInBackground()
// no php example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs
// no c# example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs
// no js example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs

If we want to find all of the people we are following, we can execute a query on the Follow table:

// set up the query on the Follow table
ParseQuery<ParseObject> query = ParseQuery.getQuery("Follow");
query.whereEqualTo("from", ParseUser.getCurrentUser());

// execute the query
query.findInBackground(newFindCallback<ParseObject>() {
    public void done(List<ParseObject> followList, ParseException e) {

    }
});
// set up the query on the Follow table
PFQuery *query = [PFQuery queryWithClassName:@"Follow"];
[query whereKey:@"from" equalTo:[PFUser currentUser]];

// execute the query
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
  for(PFObject *o in objects) {
    // o is an entry in the Follow table
    // to get the user, we get the object with the to key
    PFUser *otherUser = [o objectForKey@"to"];

    // to get the time when we followed this user, get the date key
    PFObject *when = [o objectForKey@"date"];
  }
}];
// set up the query on the Follow table
let query = PFQuery(className: "Follow")
query.whereKey("from", equalTo: PFUser.currentUser()!)

// execute the query
query.findObjectsInBackgroundWithBlock{
    (objects: [AnyObject]?, error: NSError?) -> Void in
    if let objects = objects {
        for o in objects {
            // o is an entry in the Follow table
            // to get the user, we get the object with the to key
            let otherUse = o.objectForKey("to") as? PFUser

            // to get the time when we followed this user, get the date key
            let when = o.objectForKey("date") as? PFObject
        }
    }
}
// no php example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs
// no c# example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs
// no js example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs

It’s also pretty easy to find all the users that are following the current user by querying on the to key:

// set up the query on the Follow table
ParseQuery<ParseObject> query = ParseQuery.getQuery("Follow");
query.whereEqualTo("to", ParseUser.getCurrentUser());

// execute the query
query.findInBackground(newFindCallback<ParseObject>() {
    public void done(List<ParseObject> followList, ParseException e) {

    }
});
// set up the query on the Follow table
PFQuery *query = [PFQuery queryWithClassName:@"Follow"];
[query whereKey:@"to" equalTo:[PFUser currentUser]];

[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
  for(PFObject *o in objects) {
     // o is an entry in the Follow table
     // to get the user, we get the object with the from key
    PFUser *otherUser = [o objectForKey@"from"];

    // to get the time the user was followed, get the date key
    PFObject *when = [o objectForKey@"date"];
  }
}];
// set up the query on the Follow table
let query = PFQuery(className: "Follow")
query.whereKey("to", equalTo: PFUser.currentUser()!)

query.findObjectsInBackgroundWithBlock{
    (objects: [AnyObject]?, error: NSError?) -> Void in
    if let objects = objects {
        for o in objects {
            // o is an entry in the Follow table
            // to get the user, we get the object with the to key
            let otherUse = o.objectForKey("to") as? PFUser

            // to get the time when we followed this user, get the date key
            let when = o.objectForKey("date") as? PFObject
        }

    }
}
// no php example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs
// no c# example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs
// no js example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs

Using an Array

Arrays are used in Many-to-Many relationships in much the same way that they are for One-to-Many relationships. All objects on one side of the relationship will have an Array column containing several objects on the other side of the relationship.

Suppose we have a book reading app with Book and Author objects. The Book object will contain an Array of Author objects (with a key named authors). Arrays are a great fit for this scenario because it's highly unlikely that a book will have more than 100 or so authors. We will put the Array in the Book object for this reason. After all, an author could write more than 100 books.

Here is how we save a relationship between a Book and an Author.

// let's say we have an author
ParseObject author = ...

// and let's also say we have an book
ParseObject book = ...

// add the author to the authors list for the book
book.put("authors", author);
// let's say we have an author
PFObject *author = ...

// and let's also say we have an book
PFObject *book = ...

// add the author to the authors list for the book
[book addObject:author forKey:@"authors"];
// let's say we have an author
let author = ...

// and let's also say we have an book
let book = ...

// add the author to the authors list for the book
book.addObject(author, forKey: "authors")
// no php example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs
// no c# example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs
// no js example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs

Because the author list is an Array, you should use the includeKey (or include on Android) parameter when fetching a Book so that Parse returns all the authors when it also returns the book:

// set up our query for the Book object
ParseQuery bookQuery = ParseQuery.getQuery("Book");

// configure any constraints on your query...
// tell the query to fetch all of the Author objects along with the Book
bookQuery.include("authors");

// execute the query
bookQuery.findInBackground(newFindCallback<ParseObject>() {
    public void done(List<ParseObject> bookList, ParseException e) {
    }
});
// set up our query for the Book object
PFQuery *bookQuery = [PFQuery queryWithClassName:@"Book"];

// configure any constraints on your query...
// tell the query to fetch all of the Author objects along with the Book
[bookQuery includeKey:@"authors"];

// execute the query
[bookQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
    // objects is all of the Book objects, and their associated
    // Author objects, too
}];
// set up our query for the Book object
let bookQuery = PFQuery(className: "Book")

// configure any constraints on your query...
// tell the query to fetch all of the Author objects along with the Book
bookQuery.includeKey("authors")

// execute the query
bookQuery.findObjectsInBackgroundWithBlock{
    (objects: [AnyObject]?, error: NSError?) -> Void in
    // objects is all of the Book objects, and their associated
    // Author objects, too
}
// no php example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs
// no c# example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs
// no js example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs

At that point, getting all the Author objects in a given Book is a pretty straightforward call:

ArrayList<ParseObject> authorList = book.getList("authors");
NSArray *authorList = [book objectForKey@"authors"];
let authorList = book.objectForKey("authors") as? NSArray
// no php example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs
// no c# example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs
// no js example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs

Finally, suppose you have an Author and you want to find all the Book objects in which she appears. This is also a pretty straightforward query with an associated constraint:

// set up our query for the Book object
ParseQuery bookQuery = ParseQuery.getQuery("Book");

// configure any constraints on your query...
booKQuery.whereEqualTo("authors", author);

// tell the query to fetch all of the Author objects along with the Book
bookQuery.include("authors");

// execute the query
bookQuery.findInBackground(newFindCallback<ParseObject>() {
    public void done(List<ParseObject> bookList, ParseException e) {

    }
});
// suppose we have an Author object
PFObject *author = ...

// set up our query for the Book object
PFQuery *bookQuery = [PFQuery queryWithClassName:@"Book"];

// configure any constraints on your query...
[bookQuery whereKey:@"authors" equalTo:author];

// tell the query to fetch all of the Author objects along with the Book
[bookQuery includeKey:@"authors"];

// execute the query
[bookQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
    // objects is all of the Book objects, and their associated Author objects, too
}];
// suppose we have an Author object
let author = ...

// set up our query for the Book object
let bookQuery = PFQuery(className: "Book")

// configure any constraints on your query...
bookQuery.whereKey("authors", equalTo: author)

// tell the query to fetch all of the Author objects along with the Book
bookQuery.includeKey("authors")

// execute the query
bookQuery.findObjectsInBackgroundWithBlock{
    (objects: [AnyObject]?, error: NSError?) -> Void in
    // objects is all of the Book objects, and their associated Author objects, too
}
// no php example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs
// no c# example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs
// no js example
// Want to contribute to this doc? https://github.com/ParsePlatform/Docs

One-to-One

In Parse, a one-to-one relationship is great for situations where you need to split one object into two objects. These situations should be rare, but two examples include:

Thank you for reading this far. We apologize for the complexity. Modeling relationships in data is a hard subject, in general. But look on the bright side: it's still easier than relationships with people.

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.

Error Handling

Most Parse JavaScript functions report their success or failure using an object with callbacks, similar to a Backbone "options" object. The two primary callbacks used are success and error. success is called whenever an operation completes without errors. Generally, its parameter will be either the Parse.Object in the case of save or get, or an array of Parse.Object for find.

error is called for any kind of error that occurs when interacting with the Parse Cloud over the network. These errors are either related to problems connecting to the cloud or problems performing the requested operation. Let's take a look at another example. In the code below, we try to fetch an object with a non-existent objectId. The Parse Cloud will return an error - so here's how to handle it properly in your callback:

var query = new Parse.Query(Note);
query.get("aBcDeFgH", {
  success: function(results) {
    // This function will *not* be called.
    alert("Everything went fine!");
  },
  error: function(model, error) {
    // This will be called.
    // error is an instance of Parse.Error with details about the error.
    if (error.code === Parse.Error.OBJECT_NOT_FOUND) {
      alert("Uh oh, we couldn't find the object!");
    }
  }
});

The query might also fail because the device couldn't connect to the Parse Cloud. Here's the same callback but with a bit of extra code to handle that scenario:

var query = new Parse.Query(Note);
query.get("thisObjectIdDoesntExist", {
  success: function(results) {
    // This function will *not* be called.
    alert("Everything went fine!");
  },
  error: function(model, error) {
    // This will be called.
    // error is an instance of Parse.Error with details about the error.
    if (error.code === Parse.Error.OBJECT_NOT_FOUND) {
      alert("Uh oh, we couldn't find the object!");
    } else if (error.code === Parse.Error.CONNECTION_FAILED) {
      alert("Uh oh, we couldn't even connect to the Parse Cloud!");
    }
  }
});

For methods like save and signUp that affect a particular Parse.Object, the first argument to the error function will be the object itself, and the second will be the Parse.Error object. This is for compatibility with Backbone-type frameworks.

For a list of all possible Parse.Error codes, scroll down to Error Codes, or see the Parse.Error section of the JavaScript API Parse.Error.

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.

Data

Valid Data Types

We've designed the Parse SDKs so that you typically don't need to worry about how data is saved while using the client SDKs. Simply add data to the Parse.Object, and it'll be saved correctly.

Nevertheless, there are some cases where it's useful to be aware of how data is stored on the Parse platform.

Internally, Parse stores data as JSON, so any datatype that can be converted to JSON can be stored on Parse. The framework can also handle Date and File types. Overall, the following types are allowed for each field in your object:

The type Object simply denotes that each value can be composed of nested objects that are JSON-encodable. Keys including the characters $ or ., along with the key __type key, are reserved for the framework to handle additional types, so don't use those yourself.

We do not recommend storing large pieces of binary data like images or documents in a Parse.Object. Parse.Objects should not exceed 128 kilobytes in size. We recommend you use Files to store images, documents, and other types of files. You can do so by instantiating a Parse.File object and setting it on a field.

Data Type Lock-in

When a class is initially created, it doesn't have an inherent schema defined. This means that for the first object, it could have any types of fields you want.

However, after a field has been set at least once, that field is locked into the particular type that was saved. For example, if a Parse.User object is saved with field name of type String, that field will be restricted to the String type only (our SDK will return an error if you try to save anything else).

One special case is that any field can be set to null, no matter what type it is.

The Data Browser

The Data Browser is the web UI where you can update and create objects in each of your apps. Here, you can see the raw JSON values that are saved that represents each object in your class.

When using the interface, keep in mind the following:

The Data Browser is also a great place to test the Cloud Code validations contained in your Cloud Code functions (such as beforeSave). These are run whenever a value is changed or object is deleted from the Data Browser, just as they would be if the value was changed or deleted from your client code.

Importing Data

You may import data into your Parse app by using CSV or JSON files. To create a new class with data from a CSV or JSON file, go to the Data Browser and click the "Import" button on the left hand column.

The JSON format is an array of objects in our REST format or a JSON object with a results that contains an array of objects. It must adhere to the JSON standard. A file containing regular objects could look like:

{ "results": [
  {
    "score": 1337,
    "playerName": "Sean Plott",
    "cheatMode": false,
    "createdAt": "2012-07-11T20:56:12.347Z",
    "updatedAt": "2012-07-11T20:56:12.347Z",
    "objectId": "fchpZwSuGG"
  }]
}

Objects in either format should contain keys and values that also satisfy the following:

Normally, when objects are saved to Parse, they are automatically assigned a unique identifier through the objectId field, as well as a createdAt field and updatedAt field which represent the time that the object was created and last modified in the Parse Cloud. These fields can be manually set when data is imported from a JSON file. Please keep in mind the following:

In addition to the exposed fields, objects in the Parse User class can also have the bcryptPassword field set. The value of this field is a String that is the bcrypt hashed password + salt in the modular crypt format described in this StackOverflow answer. Most OpenSSL based bcrypt implementations should have built-in methods to produce these strings.

A file containing a Parse.User object could look like:

{ "results":
  [{
    "username": "cooldude",
    "createdAt": "1983-09-13T22:42:30.548Z",
    "updatedAt": "2015-09-04T10:12:42.137Z",
    "objectId": "ttttSEpfXm",
    "sessionToken": "dfwfq3dh0zwe5y2sqv514p4ib",
    "bcryptPassword": "$2a$10$ICV5UeEf3lICfnE9W9pN9.O9Ved/ozNo7G83Qbdk5rmyvY8l16MIK"
  }]
}

Note that in CSV the import field types are limited to String, Boolean, and Number.

Exporting your Data

You can request an export of your data at any time from your app's Settings page. The data export runs at a lower priority than production queries, so if your app is still serving queries, production traffic will always be given a higher priority, which may slow down the delivery of your data export.

Export Formats

Each collection will be exported in the same JSON format used by our REST API and delivered in a single zipped file. Since data is stored internally as JSON, this allows us to ensure that the export closely matches how the data is saved to Parse. Other formats such as CSV cannot represent all of the data types supported by Parse without losing information. If you'd like to work with your data in CSV format, you can use any of the JSON-to-CSV converters available widely on the web.

Offline Analysis

For offline analysis of your data, we highly recommend using alternate ways to access your data that do not require extracting the entire collection at once. For example, you can try exporting only the data that has changed since your last export. Here are some ways of achieving this:

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.

Security

As your app development progresses, you will want to use Parse's security features in order to safeguard data. This document explains the ways in which you can secure your apps.

If your app is compromised, it's not only you as the developer who suffers, but potentially the users of your app as well. Continue reading for our suggestions for sensible defaults and precautions to take before releasing your app into the wild.

Client vs. Server

When an app first connects to Parse, it identifies itself with an Application ID and a Client key (or REST Key, or .NET Key, or JavaScript Key, depending on which platform you're using). These are not secret and by themselves they do not secure an app. These keys are shipped as a part of your app, and anyone can decompile your app or proxy network traffic from their device to find your client key. This exploit is even easier with JavaScript — one can simply "view source" in the browser and immediately find your client key.

This is why Parse has many other security features to help you secure your data. The client key is given out to your users, so anything that can be done with just the client key is doable by the general public, even malicious hackers.

The master key, on the other hand, is definitely a security mechanism. Using the master key allows you to bypass all of your app's security mechanisms, such as class-level permissions and ACLs. Having the master key is like having root access to your app's servers, and you should guard your master key with the same zeal with which you would guard your production machines' root password.

The overall philosophy is to limit the power of your clients (using client keys), and to perform any sensitive actions requiring the master key in Cloud Code. You'll learn how to best wield this power in the section titled Implementing Business Logic in Cloud Code.

A final note: All connections are made with HTTPS and SSL, and Parse will reject all non-HTTPS connections. As a result, you don't need to worry about man-in-the-middle attacks.

Class-Level Permissions

The second level of security is at the schema and data level. Enforcing security measures at this level will restrict how and when client applications can access and create data on Parse. When you first begin developing your Parse application, all of the defaults are set so that you can be a more productive developer. For example:

You can configure any of these permissions to apply to everyone, no one, or to specific users or roles in your app. Roles are groups that contain users or other roles, which you can assign to an object to restrict its use. Any permission granted to a role is also granted to any of its children, whether they are users or other roles, enabling you to create an access hierarchy for your apps. Each of the Parse guides includes a detailed description of employing Roles in your apps.

Once you are confident that you have the right classes and relationships between classes in your app, you should begin to lock it down by doing the following:

Almost every class that you create should have these permissions tweaked to some degree. For classes where every object has the same permissions, class-level settings will be most effective. For example, one common use case entails having a class of static data that can be read by anyone but written by no one.

Restricting class creation

As a start, you can configure your application so that clients cannot create new classes on Parse. This is done from the Settings tab on the Data Browser. Scroll down to the App Permissions section and turn off Allow client class creation. Once enabled, classes may only be created from the Data Browser. This will prevent attackers from filling your database with unlimited, arbitrary new classes.

Configuring Class-Level Permissions

Parse lets you specify what operations are allowed per class. This lets you restrict the ways in which clients can access or modify your classes. To change these settings, go to the Data Browser, select a class, and click the "Security" button.

You can configure the client's ability to perform each of the following operations for the selected class:

For each of the above actions, you can grant permission to all users (which is the default), or lock permissions down to a list of roles and users. For example, a class that should be available to all users would be set to read-only by only enabling get and find. A logging class could be set to write-only by only allowing creates. You could enable moderation of user-generated content by providing update and delete access to a particular set of users or roles.

Object-Level Access Control

Once you've locked down your schema and class-level permissions, it's time to think about how data is accessed by your users. Object-level access control enables one user's data to be kept separate from another's, because sometimes different objects in a class need to be accessible by different people. For example, a user’s private personal data should be accessible only to them.

Parse also supports the notion of anonymous users for those apps that want to store and protect user-specific data without requiring explicit login.

When a user logs into an app, they initiate a session with Parse. Through this session they can add and modify their own data but are prevented from modifying other users' data.

Access Control Lists

The easiest way to control who can access which data is through access control lists, commonly known as ACLs. The idea behind an ACL is that each object has a list of users and roles along with what permissions that user or role has. A user needs read permissions (or must belong to a role that has read permissions) in order to retrieve an object's data, and a user needs write permissions (or must belong to a role that has write permissions) in order to update or delete that object.

Once you have a User, you can start using ACLs. Remember: Users can be created through traditional username/password signup, through a third-party login system like Facebook or Twitter, or even by using Parse's automatic anonymous users functionality. To set an ACL on the current user's data to not be publicly readable, all you have to do is:

PFUser *user = [PFUser currentUser];
user.ACL = [PFACL ACLWithUser:user];
if let user = PFUser.currentUser() {
    user.ACL = PFACL(user: user)
}
ParseUser user = ParseUser.getCurrentUser();
user.setACL(new ParseACL(user));
var user = Parse.User.current();
user.setACL(new Parse.ACL(user));
// no c# example
// no php example

Most apps should do this. If you store any sensitive user data, such as email addresses or phone numbers, you need to set an ACL like this so that the user's private information isn't visible to other users. If an object doesn't have an ACL, it's readable and writeable by everyone. The only exception is the _User class. We never allow users to write each other's data, but they can read it by default. (If you as the developer need to update other _User objects, remember that your master key can provide the power to do this.)

To make it super easy to create user-private ACLs for every object, we have a way to set a default ACL that will be used for every new object you create:

[PFACL setDefaultACL:[PFACL ACL] withAccessForCurrentUser:YES];
PFACL.setDefaultACL(PFACL(), withAccessForCurrentUser: true)
ParseACL.setDefaultACL(new ParseACL(), true);
// no js example
// no c# example
// no php example

If you want the user to have some data that is public and some that is private, it's best to have two separate objects. You can add a pointer to the private data from the public one.

PFObject *privateData = [PFObject objectWithClassName:@"PrivateUserData"];
privateData.ACL = [PFACL ACLWithUser:[PFUser currentUser]];
[privateData setObject:@"555-5309" forKey:@"phoneNumber"];

[[PFUser currentUser] setObject:privateData forKey:@"privateData"];
if let currentUser = PFUser.currentUser() {
    let privateData = PFObject(className: "PrivateUserData")
    privateData.ACL = PFACL(user: currentUser)
    privateData.setObject("555-5309", forKey: "phoneNumber")
    currentUser.setObject(privateData, forKey: "privateData")
}
ParseObject privateData = new ParseObject("PrivateUserData");
privateData.setACL(new ParseACL(ParseUser.getCurrentUser()));
privateData.put("phoneNumber", "555-5309");

ParseUser.getCurrentUser().put("privateData", privateData);
var privateData = Parse.Object.extend("PrivateUserData");
privateData.setACL(new Parse.ACL(Parse.User.current()));
privateData.set("phoneNumber", "555-5309");

Parse.User.current().set("privateData", privateData);
// no c# example
// no php example

Of course, you can set different read and write permissions on an object. For example, this is how you would create an ACL for a public post by a user, where anyone can read it:

PFACL *acl = [PFACL ACL];
[acl setPublicReadAccess:true];
[acl setWriteAccess:true forUser:[PFUser currentUser]];
let acl = PFACL()
acl.setPublicReadAccess(true)
if let currentUser = PFUser.currentUser() {
    acl.setWriteAccess(true, forUser: currentUser)
}
ParseACL acl = new ParseACL();
acl.setPublicReadAccess(true);
acl.setWriteAccess(ParseUser.getCurrentUser(), true);
var acl = new Parse.ACL();
acl.setPublicReadAccess(true);
acl.setWriteAccess(Parse.User.current().id, true);
// no c# example
// no php example

Sometimes it's inconvenient to manage permissions on a per-user basis, and you want to have groups of users who get treated the same (like a set of admins with special powers). Roles are are a special kind of object that let you create a group of users that can all be assigned to the ACL. The best thing about roles is that you can add and remove users from a role without having to update every single object that is restricted to that role. To create an object that is writeable only by admins:

// Assuming you've already created a role called "admins"...
PFACL *acl = [PFACL ACL];
[acl setPublicReadAccess:true];
[acl setWriteAccess:true forRoleWithName:@"admins"];
let acl = PFACL()
acl.setPublicReadAccess(true)
acl.setWriteAccess(true, forRoleWithName: "admins")
// Assuming you've already created a role called "admins"...
ParseACL acl = new ParseACL();
acl.setPublicReadAccess(true);
acl.setRoleWriteAccess("admins", true);
var acl = new Parse.ACL();
acl.setPublicReadAccess(true);
acl.setRoleWriteAccess("admins", true);
// no c# example
// no php example

Of course, this snippet assumes you've already created a role named "admins". This is often reasonable when you have a small set of special roles set up while developing your app. Roles can also be created and updated on the fly — for example, adding new friends to a "friendOf_" role after each connection is made.

All this is just the beginning. Applications can enforce all sorts of complex access patterns through ACLs and class-level permissions. For example:

For the curious, here's the format for an ACL that restricts read and write permissions to the owner (whose objectId is identified by "aSaMpLeUsErId") and enables other users to read the object:

{
    "*": { "read":true },
    "aSaMpLeUsErId": { "read" :true, "write": true }
}

And here's another example of the format of an ACL that uses a Role:

{
    "role:RoleName": { "read": true },
    "aSaMpLeUsErId": { "read": true, "write": true }
}

Pointer Permissions

Pointer permissions are a special type of class-level permission that create a virtual ACL on every object in a class, based on users stored in pointer fields on those objects. For example, given a class with an owner field, setting a read pointer permission on owner will make each object in the class only readable by the user in that object's owner field. For a class with a sender and a reciever field, a read pointer permission on the sender field and a read and write pointer permission on the receiver field will make each object in the class readable by the user in the sender and receiver field, and writable only by the user in the sender field.

Given that objects often already have pointers to the user(s) that should have permissions on the object, pointer permissions provide a simple and fast solution for securing your app using data which is already there, that doesn't require writing any client code or cloud code.

Pointer permissions are like virtual ACLs. They don't appear in the ACL column, buf if you are familiar with how ACLs work, you can think of them like ACLs. In the above example with the sender and receiver, each object will act as if it has an ACL of:

{
    "<SENDER_USER_ID>": {
        "read": true,
        "write": true
    },
    "<RECEIVER_USER_ID>": {
        "read": true
    }
}

Note that this ACL is not actually created on each object. Any existing ACLs will not be modified when you add or remove pointer permissions, and any user attempting to interact with an object can only interact with the object if both the virtual ACL created by the pointer permissions, and the real ACL already on the object allow the interaction. For this reason, it can sometimes be confusing to combine pointer permissions and ACLs, so we recommend using pointer permissions for classes that don't have many ACLs set. Fortunately, it's easy to remove pointer permissions if you later decide to use Cloud Code or ACLs to secure your app.

CLP and ACL interaction

Class-Level Permissions (CLPs) and Access Control Lists (ACLs) are both powerful tools for securing your app, but they don't always interact exactly how you might expect. They actually represent two separate layers of security that each request has to pass through to return the correct information or make the intended change. These layers, one at the class level, and one at the object level, are shown below. A request must pass through BOTH layers of checks in order to be authorized. Note that despite acting similarly to ACLs, Pointer Permissions are a type of class level permission, so a request must pass the pointer permission check in order to pass the CLP check.

As you can see, whether a user is authorized to make a request can become complicated when you use both CLPs and ACLs. Let's look at an example to get a better sense of how CLPs and ACLs can interact. Say we have a Photo class, with an object, photoObject. There are 2 users in our app, user1 and user2. Now lets say we set a Get CLP on the Photo class, disabling public Get, but allowing user1 to perform Get. Now let's also set an ACL on photoObject to allow Read - which includes GET - for only user2.

You may expect this will allow both user1 and user2 to Get photoObject, but because the CLP layer of authentication and the ACL layer are both in effect at all times, it actually makes it so neither user1 nor user2 can Get photoObject. If user1 tries to Get photoObject, it will get through the CLP layer of authentication, but then will be rejected because it does not pass the ACL layer. If user2tries to Get photoObject, it will be rejected at the CLP layer of authentication.

Now lets look at example that uses Pointer Permissions. Say we have a Post class, with an object, myPost. There are 2 users in our app, poster, and viewer. Lets say we add a pointer permission that gives anyone in the Creator field of the Post class read and write access to the object, and for the myPost object, poster is the user in that field. There is also an ACL on the object that gives read access to viewer. You may expect that this will allow poster to read and edit myPost, and viewer to read it, but viewer will be rejected by the Pointer Permission, and poster will be rejected by the ACL, so again, neither user will be able to access the object.

Because of the complex interaction between CLPs, Pointer Permissions, and ACLs, we recommend being careful when using them together. Often it can be useful to use CLPs only to disable all permissions for a certain request type, and then using Pointer Permissions or ACLs for other request types. For example, you may want to disable Delete for a Photo class, but then put a Pointer Permission on Photo so the user who created it can edit it, just not delete it. Because of the especially complex way that Pointer Permissions and ACLs interact, we usually recommend only using one of those two types of security mechanisms.

Security Edge Cases

There are some special classes in Parse that don't follow all of the same security rules as every other class. Not all classes follow Class-Level Permissions (CLPs) or Access Control Lists (ACLs) exactly how they are defined, and here those exceptions are documented. Here "normal behavior" refers to CLPs and ACLs working normally, while any other special behaviors are described in the footnotes.

_User _Installation
Get normal behaviour [1, 2, 3] ignores CLP, but not ACL
Find normal behavior [3] master key only [6]
Create normal behavior [4] ignores CLP
Update normal behavior [5] ignores CLP, but not ACL [7]
Delete normal behavior [5] master key only [7]
Add Field normal behavior normal behavior
  1. Logging in, or /1/login in the REST API, does not respect the Get CLP on the user class. Login works just based on username and password, and cannot be disabled using CLPs.

  2. Retrieving the current user, or becoming a User based on a session token, which are both /1/users/me in the REST API, do not respect the Get CLP on the user class.

  3. Read ACLs do not apply to the logged in user. For example, if all users have ACLs with Read disabled, then doing a find query over users will still return the logged in user. However, if the Find CLP is disabled, then trying to perform a find on users will still return an error.

  4. Create CLPs also apply to signing up. So disabling Create CLPs on the user class also disables people from signing up without the master key.

  5. Users can only Update and Delete themselves. Public CLPs for Update and Delete may still apply. For example, if you disable public Update for the user class, then users cannot edit themselves. But no matter what the write ACL on a user is, that user can still Update or Delete itself, and no other user can Update or Delete that user. As always, however, using the master key allows users to update other users, independent of CLPs or ACLs.

  6. Get requests on installations follow ACLs normally. Find requests, however, return the installation making the request, and only that installation, no matter what ACL is defined on that installation.

  7. Update requests on installations do adhere to the ACL defined on the installation, but Delete requests are master-key-only. For more information about how installations work, check out the installations section of the REST guide.

Data Integrity in Cloud Code

For most apps, care around keys, class-level permissions, and object-level ACLs are all you need to keep your app and your users' data safe. Sometimes, though, you'll run into an edge case where they aren't quite enough. For everything else, there's Cloud Code.

Cloud Code allows you to upload JavaScript to Parse's servers, where we will run it for you. Unlike client code running on users' devices that may have been tampered with, Cloud Code is guaranteed to be the code that you've written, so it can be trusted with more responsibility.

One particularly common use case for Cloud Code is preventing invalid data from being stored. For this sort of situation, it's particularly important that a malicious client not be able to bypass the validation logic.

To create validation functions, Cloud Code allows you to implement a beforeSave trigger for your class. These triggers are run whenever an object is saved, and allow you to modify the object or completely reject a save. For example, this is how you create a Cloud Code beforeSave trigger to make sure every user has an email address set:

Parse.Cloud.beforeSave(Parse.User, function(request, response) {
  var user = request.object;
  if (!user.get("email")) {
    response.error("Every user must have an email address.");
  } else {
    response.success();
  }
});

Our Cloud Code guide provides instructions on how to upload this trigger to our servers.

Validations can lock down your app so that only certain values are acceptable. You can also use afterSave validations to normalize your data (e.g. formatting all phone numbers or currency identically). You get to retain most of the productivity benefits of accessing Parse data directly from your client applications, but you can also enforce certain invariants for your data on the fly.

Common scenarios that warrant validation include:

Implementing Business Logic in Cloud Code

While validation often makes sense in Cloud Code, there are likely certain actions that are particularly sensitive, and should be as carefully guarded as possible. In these cases, you can remove permissions or the logic from clients entirely and instead funnel all such operations to Cloud Code functions.

When a Cloud Code function is called, it can invoke the useMasterKey function to gain the ability to modify user data. With the master key, your Cloud Code function can override any ACLs and write data. This means that it'll bypass all the security mechanisms you've put in place in the previous sections.

Say you want to allow a user to "like" a Post object without giving them full write permissions on the object. You can do this by having the client call a Cloud Code function instead of modifying the Post itself:

The master key should be used carefully. When invoked, the master key is in effect for the duration of the Cloud Code function in which it is called:

Parse.Cloud.define("like", function(request, response) {
  Parse.Cloud.useMasterKey();
  // Everything after this point will bypass ACLs and other security
  // even if I do things besides just updating a Post object.
});

A more prudent way to use the master key would be to pass it as a parameter on a per-function basis. For example, instead of the above, set useMasterKey to true in each individual API function:

Parse.Cloud.define("like", function(request, response) {
  var post = new Parse.Object("Post");
  post.id = request.params.postId;
  post.increment("likes");
  post.save(null, { useMasterKey: true }).then(function() {
    // If I choose to do something else here, it won't be using
    // the master key and I'll be subject to ordinary security measures.
    response.success();
  }, function(error) {
    response.error(error);
  });
});

One very common use case for Cloud Code is sending push notifications to particular users. In general, clients can't be trusted to send push notifications directly, because they could modify the alert text, or push to people they shouldn't be able to. Your app's settings will allow you to set whether "client push" is enabled or not; we recommend that you make sure it's disabled. Instead, you should write Cloud Code functions that validate the data to be pushed and sender before sending a push.

Parse Security Summary

Parse provides a number of ways for you to secure data in your app. As you build your app and evaluate the kinds of data you will be storing, you can make the decision about which implementation to choose.

It is worth repeating that that the Parse User object is readable by all other users by default. You will want to set the ACL on your User object accordingly if you wish to prevent data contained in the User object (for example, the user's email address) from being visible by other users.

Most classes in your app will fall into one of a couple of easy-to-secure categories. For fully public data, you can use class-level permissions to lock down the table to put publicly readable and writeable by no one. For fully private data, you can use ACLs to make sure that only the user who owns the data can read it. But occasionally, you'll run into situations where you don't want data that’s fully public or fully private. For example, you may have a social app, where you have data for a user that should be readable only to friends whom they’ve approved. For this you'll need to a combination of the techniques discussed in this guide to enable exactly the sharing rules you desire.

We hope that you'll use these tools to do everything you can to keep your app's data and your users' data secure. Together, we can make the web a safer place.

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.

Push Notifications

If you haven't installed the SDK yet, please head over to the Push QuickStart to get our SDK up and running.

Introduction

Push Notifications are a great way to keep your users engaged and informed about your app. You can reach your entire user base quickly and effectively. This guide will help you through the setup process and the general usage of Parse to send push notifications.

The JavaScript SDK does not currently support receiving pushes. It can only be used to send notifications to iOS and Android applications. A common use case is to send pushes from Cloud Code.

Setting Up Push

There is no setup required to use the JavaScript SDK for sending push notifications. If you haven't configured your iOS or Android clients to use Push, take a look at their respective setup instruction using the platform toggle at the top.

Installations

Every Parse application installed on a device registered for push notifications has an associated Installation object. The Installation object is where you store all the data needed to target push notifications. For example, in a baseball app, you could store the teams a user is interested in to send updates about their performance.

Note that Installation data can only be modified by the client SDKs, the data browser, or the REST API.

This class has several special fields that help you manage and target devices.

Sending Pushes

There are two ways to send push notifications using Parse: channels and advanced targeting. Channels offer a simple and easy to use model for sending pushes, while advanced targeting offers a more powerful and flexible model. Both are fully compatible with each other and will be covered in this section.

Sending notifications is often done from the Parse.com push console, the REST API or from Cloud Code. Since the JavaScript SDK is used in Cloud Code, this is the place to start if you want to send pushes from your Cloud Functions. However, if you decide to send notifications from the JavaScript SDK outside of Cloud Code or any of the other client SDKs, you will need to set Client Push Enabled in the Push Notifications settings of your Parse app.

However, be sure you understand that enabling Client Push can lead to a security vulnerability in your app, as outlined on our blog. We recommend that you enable Client Push for testing purposes only, and move your push notification logic into Cloud Code when your app is ready to go into production.

You can view your past push notifications on the Parse.com push console for up to 30 days after creating your push. For pushes scheduled in the future, you can delete the push on the push console as long as no sends have happened yet.

After you send the push, the push console shows push analytics graphs.

Using Channels

The simplest way to start sending notifications is using channels. This allows you to use a publisher-subscriber model for sending pushes. Devices start by subscribing to one or more channels, and notifications can later be sent to these subscribers. The channels subscribed to by a given Installation are stored in the channels field of the Installation object.

Subscribing to Channels

The JavaScript SDK does not currently support subscribing iOS and Android devices for pushes. Take a look at the iOS, Android or REST Push guide using the platform toggle at the top.

Sending Pushes to Channels

With the JavaScript SDK, the following code can be used to alert all subscribers of the "Giants" and "Mets" channels about the results of the game. This will display a notification center alert to iOS users and a system tray notification to Android users.

Parse.Push.send({
  channels: [ "Giants", "Mets" ],
  data: {
    alert: "The Giants won against the Mets 2-3."
  }
}, {
  success: function() {
    // Push was successful
  },
  error: function(error) {
    // Handle error
  }
});

Using Advanced Targeting

While channels are great for many applications, sometimes you need more precision when targeting the recipients of your pushes. Parse allows you to write a query for any subset of your Installation objects using the querying API and to send them a push.

Since Installation objects are just like any other object stored in Parse, you can save any data you want and even create relationships between Installation objects and your other objects. This allows you to send pushes to a very customized and dynamic segment of your user base.

Saving Installation Data

The JavaScript SDK does not currently support modifying Installation objects. Take a look at the iOS, Android or REST Push guide for more on this topic.

Sending Pushes to Queries

Once you have your data stored on your Installation objects, you can use a query to target a subset of these devices. Parse.Installation queries work just like any other Parse query.

var query = new Parse.Query(Parse.Installation);
query.equalTo('injuryReports', true);

Parse.Push.send({
  where: query, // Set our Installation query
  data: {
    alert: "Willie Hayes injured by own pop fly." 
  }
}, {
  success: function() {
    // Push was successful
  },
  error: function(error) {
    // Handle error
  }
});

We can even use channels with our query. To send a push to all subscribers of the "Giants" channel but filtered by those who want score update, we can do the following:

var query = new Parse.Query(Parse.Installation);
query.equalTo('channels', 'Giants'); // Set our channel
query.equalTo('scores', true);

Parse.Push.send({
  where: query,
  data: {
    alert: "Giants scored against the A's! It's now 2-2." 
  }
}, {
  success: function() {
    // Push was successful
  },
  error: function(error) {
    // Handle error
  }
});

If we store relationships to other objects in our Installation class, we can also use those in our query. For example, we could send a push notification to all users near a given location like this.

// Find users near a given location
var userQuery = new Parse.Query(Parse.User);
userQuery.withinMiles("location", stadiumLocation, 1.0);

// Find devices associated with these users
var pushQuery = new Parse.Query(Parse.Installation);
pushQuery.matchesQuery('user', userQuery);

// Send push notification to query
Parse.Push.send({
  where: pushQuery,
  data: {
    alert: "Free hotdogs at the Parse concession stand!" 
  }
}, {
  success: function() {
    // Push was successful
  },
  error: function(error) {
    // Handle error
  }
});

Sending Options

Push notifications can do more than just send a message. In iOS, pushes can also include the sound to be played, the badge number to display as well as any custom data you wish to send. In Android, it is even possible to specify an Intent to be fired upon receipt of a notification. An expiration date can also be set for the notification in case it is time sensitive.

Customizing your Notifications

If you want to send more than just a message, you can set other fields in the data dictionary. There are some reserved fields that have a special meaning.

For example, to send a notification that increases the current badge number by 1 and plays a custom sound for iOS devices, and displays a particular title for Android users, you can do the following:

Parse.Push.send({
  channels: [ "Mets" ],
  data: {
    alert: "The Mets scored! The game is now tied 1-1.",
    badge: "Increment",
    sound: "cheering.caf",
    title: "Mets Score!"
  }
}, {
  success: function() {
    // Push was successful
  },
  error: function(error) {
    // Handle error
  }
});

It is also possible to specify your own data in this dictionary. As explained in the Receiving Notifications section for iOS and Android, iOS will give you access to this data only when the user opens your app via the notification and Android will provide you this data in the Intent if one is specified.

var query = new Parse.Query(Parse.Installation);
query.equalTo('channels', 'Indians');
query.equalTo('injuryReports', true);

Parse.Push.send({
  where: query,
  data: {
    action: "com.example.UPDATE_STATUS"
    alert: "Ricky Vaughn was injured in last night's game!",
    name: "Vaughn",
    newsItem: "Man bites dog"
  }
}, {
  success: function() {
    // Push was successful
  },
  error: function(error) {
    // Handle error
  }
});

Setting an Expiration Date

When a user's device is turned off or not connected to the internet, push notifications cannot be delivered. If you have a time sensitive notification that is not worth delivering late, you can set an expiration date. This avoids needlessly alerting users of information that may no longer be relevant.

There are two parameters provided by Parse to allow setting an expiration date for your notification. The first is expiration_time which takes a Date specifying when Parse should stop trying to send the notification. To expire the notification exactly 1 week from now, you can use the following:

var oneWeekAway = new Date(...);

Parse.Push.send({
  where: everyoneQuery,
  expiration_time: oneWeekAway,
  data: {
    alert: "Season tickets on sale until next week!"
  }
}, {
  success: function() {
    // Push was successful
  },
  error: function(error) {
    // Handle error
  }
});

Alternatively, you can use the expiration_interval parameter to specify a duration of time before your notification expires. This value is relative to the push_time parameter used to schedule notifications. This means that a push notification scheduled to be sent out in 1 day and an expiration interval of 6 days can be received up to a week from now.

var oneDayAway = new Date(...);
var sixDaysAwayEpoch = (new Date(...)).getTime();

Parse.Push.send({
  push_time: oneDayAway,
  expiration_interval: sixDaysAwayEpoch,
  data: {
    alert: "Season tickets on sale until next week!"
  }
}, {
  success: function() {
    // Push was successful
  },
  error: function(error) {
    // Handle error
  }
});

Targeting by Platform

If you build a cross platform app, it is possible you may only want to target iOS or Android devices. There are two methods provided to filter which of these devices are targeted. Note that both platforms are targeted by default.

The following examples would send a different notification to Android and iOS users.

// Notification for Android users
var queryAndroid = new Parse.Query(Parse.Installation);
queryAndroid.equalTo('deviceType', 'android');

Parse.Push.send({
  where: queryAndroid,
  data: {
    alert: "Your suitcase has been filled with tiny robots!" 
  }
});

// Notification for iOS users
var queryIOS = new Parse.Query(Parse.Installation);
queryIOS.equalTo('deviceType', 'ios');

Parse.Push.send({
  where: queryIOS,
  data: {
    alert: "Your suitcase has been filled with tiny apples!" 
  }
});

// Notification for Windows 8 users
var queryWindows = new Parse.Query(Parse.Installation);
queryWindows.equalTo('deviceType', 'winrt');

Parse.Push.send({
  where: queryWindows,
  data: {
    alert: "Your suitcase has been filled with tiny glass!" 
  }
});

// Notification for Windows Phone 8 users
var queryWindowsPhone = new Parse.Query(Parse.Installation);
queryWindowsPhone.equalTo('deviceType', 'winphone');

Parse.Push.send({
  where: queryWindowsPhone,
  data: {
    alert: "Your suitcase is very hip; very metro." 
  }
});

Scheduling Pushes

You can schedule a push in advance by specifying a push_time. For example, if a user schedules a game reminder for a game tomorrow at noon UTC, you can schedule the push notification by sending:

var tomorrowDate = new Date(...);

var query = new Parse.Query(Parse.Installation);
query.equalTo('user_id', 'user_123');

Parse.Push.send({
  where: query,
  data: {
    alert: "You previously created a reminder for the game today" 
  },
  push_time: tomorrowDate
}, {
  success: function() {
    // Push was successful
  },
  error: function(error) {
    // Handle error
  }
});

If you also specify an expiration_interval, it will be calculated from the scheduled push time, not from the time the push is submitted. This means a push scheduled to be sent in a week with an expiration interval of a day will expire 8 days after the request is sent.

The scheduled time cannot be in the past, and can be up to two weeks in the future. It can be an ISO 8601 date with a date, time, and timezone, as in the example above, or it can be a numeric value representing a UNIX epoch time in seconds (UTC). To schedule an alert for 08/22/2015 at noon UTC time, you can set the push_time to either 2015-08-022T12:00:00.000Z or 1440226800000.

Receiving Pushes

The JavaScript SDK does not currently support receiving pushes. To learn more about handling received notifications in iOS or Android, use the platform toggle at the top.

Troubleshooting

For tips on troubleshooting push notifications, check the troubleshooting sections for iOS, Android, and .NET using the platform toggle at the top.

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.

Config

Parse.Config is a great way to configure your applications remotely by storing a single configuration object on Parse. It enables you to add things like feature gating or a simple "Message of the Day". To start using Parse.Config you need to add a few key/value pairs (parameters) to your app on the Parse Config Dashboard.

After that you will be able to fetch the Parse.Config on the client, like in this example:

Parse.Config.get().then(function(config) {
  var winningNumber = config.get("winningNumber");
  var message = "Yay! The number is " + winningNumber + "!";
  console.log(message);
}, function(error) {
  // Something went wrong (e.g. request timed out)
});

Retrieving Config

ParseConfig is built to be as robust and reliable as possible, even in the face of poor internet connections. Caching is used by default to ensure that the latest successfully fetched config is always available. In the below example we use get to retrieve the latest version of config from the server, and if the fetch fails we can simply fall back to the version that we successfully fetched before via current.

Parse.Config.get().then(function(config) {
  console.log("Yay! Config was fetched from the server.");

  var welcomeMessage = config.get("welcomeMessage");
  console.log("Welcome Message = " + welcomeMessage);
}, function(error) {
  console.log("Failed to fetch. Using Cached Config.");

  var config = Parse.Config.current();
  var welcomeMessage = config.get("welcomeMessage");
  if (welcomeMessage === undefined) {
    welcomeMessage = "Welcome!";
  }
  console.log("Welcome Message = " + welcomeMessage);
});

Current Config

Every Parse.Config instance that you get is always immutable. When you retrieve a new Parse.Config in the future from the network, it will not modify any existing Parse.Config instance, but will instead create a new one and make it available via Parse.Config.current(). Therefore, you can safely pass around any current() object and safely assume that it will not automatically change.

It might be troublesome to retrieve the config from the server every time you want to use it. You can avoid this by simply using the cached current() object and fetching the config only once in a while.

// Fetches the config at most once every 12 hours per app runtime
var refreshConfig = function() {
  var lastFetchedDate;
  const configRefreshInterval = 12 * 60 * 60 * 1000;
  return function() {
    var currentDate = new Date();
    if (lastFetchedDate === undefined ||
        currentDate.getTime() - lastFetchedDate.getTime() > configRefreshInterval) {
      Parse.Config.get();
      lastFetchedDate = currentDate;
    }
  };
}();

Parameters

ParseConfig supports most of the data types supported by Parse.Object:

We currently allow up to 100 parameters in your config and a total size of 128KB across all parameters.

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.

Analytics

Parse provides a number of hooks for you to get a glimpse into the ticking heart of your app. We understand that it's important to understand what your app is doing, how frequently, and when.

While this section will cover different ways to instrument your app to best take advantage of Parse's analytics backend, developers using Parse to store and retrieve data can already take advantage of metrics on Parse.

Without having to implement any client-side logic, you can view real-time graphs and breakdowns (by device type, Parse class name, or REST verb) of your API Requests in your app's dashboard and save these graph filters to quickly access just the data you're interested in.

Custom Analytics

Parse.Analytics also allows you to track free-form events, with a handful of string keys and values. These extra dimensions allow segmentation of your custom events via your app's Dashboard.

Say your app offers search functionality for apartment listings, and you want to track how often the feature is used, with some additional metadata.

var dimensions = {
  // Define ranges to bucket data points into meaningful segments
  priceRange: '1000-1500',
  // Did the user filter the query?
  source: 'craigslist',
  // Do searches happen more often on weekdays or weekends?
  dayType: 'weekday'
};
// Send the dimensions to Parse along with the 'search' event
Parse.Analytics.track('search', dimensions);

Parse.Analytics can even be used as a lightweight error tracker — simply invoke the following and you'll have access to an overview of the rate and frequency of errors, broken down by error code, in your application:

var codeString = '' + error.code;
Parse.Analytics.track('error', { code: codeString });

Note that Parse currently only stores the first eight dimension pairs per call to Parse.Analytics.track().

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

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.

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. You can learn more about the features of the command line tool in the Command Line Tool guide.

Installing the Command Line Tool

Mac/Linux

In Mac OS and Linux/Unix environments, you can get the parse 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.

Setting Up Cloud Code

The next step is to create a directory to store the code that you will run in the cloud. The command parse new sets up this directory, and will prompt you to pick which app you are creating Cloud Code for:

$ parse new MyCloudCode
Email: ninja@gmail.com
Password:
1:MyApp
Select an App: 1
$ cd MyCloudCode

Use the email address and password for your Parse account to log in. If you signed up via OAuth and never set a password, you should now set one by editing your account settings. This will create a directory called MyCloudCode in the current directory. Several files are automatically created for you:

-config/
  global.json
-cloud/
  main.js
-public/
  index.html

The config directory contains a JSON configuration file that you shouldn't normally need to deal with, 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. Keep in mind that these files will contain keys you want to keep private.

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:

$ parse deploy

To run this function once it's deployed, run:

ParseCloud.callFunctionInBackground("hello", new HashMap<String, Object>(), new FunctionCallback<String>() {
  void done(String result, ParseException e) {
    if (e == null) {
      // result is "Hello world!"
    }
  }
});
[PFCloud callFunctionInBackground:@"hello"
                   withParameters:@{}
                            block:^(NSString *result, NSError *error) {
   if (!error) {
     // result is @"Hello world!"
   }
}];
PFCloud.callFunctionInBackground("hello", withParameters: nil) {
  (response: AnyObject?, error: NSError?) -> Void in
  let responseString = response as? String
}
$result = ParseCloud::run("hello", []);
ParseCloud.CallFunctionAsync<IDictionary<string, object>>("hello", new Dictionary<string, object>()).ContinueWith(t => {
  var result = t.Result;
// result is "Hello world!"
});
Parse.Cloud.run('hello', {}, {
  success: function(result) {
    // result is 'Hello world!'
  },
  error: function(error) {
  }
});

You should see this response:

{ "result": "Hello world!" }

Congratulations! You have 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. We'll go over some examples in more detail below.

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 (go back to our list of Platforms to switch SDKs). For example, to call the Cloud function named averageStars with a parameter named movie:

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
       }
   }
});
[PFCloud callFunctionInBackground:@"averageStars"
                   withParameters:@{@"movie": @"The Matrix"}
                            block:^(NSNumber *ratings, NSError *error) {
  if (!error) {
     // ratings is 4.5
  }
}];
PFCloud.callFunctionInBackground("averageRatings", withParameters: ["movie":"The Matrix"]) {
  (response: AnyObject?, error: NSError?) -> Void in
  let ratings = response as? Float
  // ratings is 4.5
}
$ratings = ParseCloud::run("averageRatings", ["movie" => "The Matrix"]);
// $ratings is 4.5
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
});
Parse.Cloud.run('averageStars', { movie: 'The Matrix' }, {
  success: function(ratings) {
    // ratings should be 4.5
  },
  error: function(error) {
  }
});

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.id);
  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.

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);
  });

});

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:

Modules

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 exports object is returned. For example, if cloud/name.js has the following source:

var coolNames = ['Ralph', 'Skippy', 'Chip', 'Ned', 'Scooter'];
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.

Cloud Function Webhooks

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

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.

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 moview 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.

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.

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.

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. 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.
  3. 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.
  4. 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:

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" \
  -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 arbitraty 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:

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.

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. 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:

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.

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

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.

With Parse, you have a choice of building either a multi-page web app with Express, or a single-page Backbone app with the Parse JavaScript SDK on the client side. The Express approach has the advantage that your website can be crawled by search engines, while the client-side Backbone approach may result in more responsive apps. You can find details about Express and Cloud Code in our Express API docs.

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

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 express

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:

In addition, we've provided the following custom middleware:

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.

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.

Cloud Code Modules

Cloud Modules are the easiest way to integrate your Parse app with third-party services and libraries. Read on to learn how to add amazing features to your Parse app, from text messaging to email.

Cloud Modules work just like the JavaScript modules you can create yourself but they are readily available to everyone. You just add require('cloudModuleName') to your Cloud Code and you are ready to go! This guide will help you get started with the current set of available Cloud Modules. If you don't see the service you're interested in, you can take a look at our tutorial on how to create your own modules to integrate with your favorite APIs.

App Links

We provide Express middleware for generation of App Link metadata, which allows other apps to deep link into your app based on content on your website. You can check out the full documentation of App Links here. To use App Links, you require both express and applinks-metatag modules and create an Express app.

var Express = require('express');
var AppLinks = require('applinks-metatag');
var app = Express();

The middleware accepts an array of JavaScript objects that must contain a platform name (e.g. "ios", "iphone", "android", or "windows_phone") and a set of fields relevant to the platform as specified on the App Links official website. You can also provide a function that will generate the same metadata based upon the request, so that you can provide different app links data for each page.

app.use(AppLinks([{
  platform: "ios",
  url: "anypic://",
  app_name: "AnyPic"
}, {
  platform: "android",
  url: "anypic://",
  package: "com.parse.anypic"
}]));

The code snippet above will inject the following code into the content of your page's head tag:

<meta property="al:ios">
<meta property="al:ios:url" content="anypic://">
<meta property="al:ios:app_name" content="AnyPic">
<meta property="al:android">
<meta property="al:android:url" content="anypic://">
<meta property="al:android:package" content="com.parse.anypic">

You can also inject the App Links metadata into individual pages:

app.use(AppLinks([{
  platform: "ios",
  url: "anypic://",
  app_name: "AnyPic"
}, {
  platform: "android",
  url: "anypic://",
  package: "com.parse.anypic"
}]));

app.get('/home', AppLinks({
  platform: "ipad",
  url: "anypic2://",
  app_name: "AnyPic for iPad"
}), yourRenderFunction);

This will include the global iOS and Android metadata, but will also add iPad App Link metadata when rendering /home:

<meta property="al:ipad">
<meta property="al:ipad:url" content="anypic2://">
<meta property="al:ipad:app_name" content="AnyPic for iPad">
<meta property="al:ios">
<meta property="al:ios:url" content="anypic://">
<meta property="al:ios:app_name" content="AnyPic">
<meta property="al:android">
<meta property="al:android:url" content="anypic://">
<meta property="al:android:package" content="com.parse.anypic">

You can also generate App Link metadata based on request parameters by supplying a function:

app.get('/image/:imageId', AppLinks(function(req, res) {
  return {
    platform: "ipad",
    url: "anypic://image/" + req.params.imageId,
    app_name: "AnyPic for iPad"
  };
}), yourRenderFunction);

This will inject the following code when rendering /image/parsaritas:

<meta property="al:ipad">
<meta property="al:ipad:url" content="anypic://image/parsaritas">
<meta property="al:ipad:app_name" content="AnyPic for iPad">

Mailgun

Mailgun is a set of powerful APIs that allow you to send, receive, track and store email effortlessly. You can check out their service at www.mailgun.com. To use this Cloud Module, you will need to head over to the Mailgun website and create an account.

The current version of the Mailgun Cloud Module supports sending emails. To use it in your Cloud Code functions, start by requiring the module and initializing it with your credentials.

var Mailgun = require('mailgun');
Mailgun.initialize('myDomainName', 'myAPIKey');

You can then use the sendEmail function to fire off some emails. This function takes two parameters. The first is a hash with the Mailgun parameters you want to include in the request. The typical ones are from, to, subject and text, but you can find the full list on their documentation page. The second parameter to this function is an object with a success and an error field containing two callback functions.

Mailgun.sendEmail({
  to: "email@example.com",
  from: "Mailgun@CloudCode.com",
  subject: "Hello from Cloud Code!",
  text: "Using Parse and Mailgun is great!"
}, {
  success: function(httpResponse) {
    console.log(httpResponse);
    response.success("Email sent!");
  },
  error: function(httpResponse) {
    console.error(httpResponse);
    response.error("Uh oh, something went wrong");
  }
});

For additional information about the Mailgun Cloud Module, take a look at the API Reference.

Mandrill

Mandrill provides a great platform for sending transactional email. It runs on the delivery infrastructure that powers MailChimp. You can check out their service on their website. To use this Cloud Module, you will need to head over to the Mandrill website and create an account.

The current version of the Mandrill Cloud Module supports sending emails. To use it in your Cloud Code functions, start by requiring the module and initializing it with your credentials.

var Mandrill = require('mandrill');
Mandrill.initialize('myAPIKey');

You can then use the sendEmail function to fire off some emails. This function takes two parameters. The first is a hash with the Mandrill parameters you want to include in the request. A full list is available on their documentation page, but here is an example of the common ones. The message object is required with every request and contains the email's data such as the text, the subject and an array of recipients. Optionally, the async boolean value can be provided to ensure the request returns once the email is queued instead of waiting until it is sent. If you are sending an email to multiple recipients, you should set async to true to ensure your cloud function does not timeout. The second parameter to this function is an object with a success and an error field containing two callback functions for the request.

Mandrill.sendEmail({
  message: {
    text: "Hello World!",
    subject: "Using Cloud Code and Mandrill is great!",
    from_email: "parse@cloudcode.com",
    from_name: "Cloud Code",
    to: [
      {
        email: "you@parse.com",
        name: "Your Name"
      }
    ]
  },
  async: true
},{
  success: function(httpResponse) {
    console.log(httpResponse);
    response.success("Email sent!");
  },
  error: function(httpResponse) {
    console.error(httpResponse);
    response.error("Uh oh, something went wrong");
  }
});

For additional information about the Mandrill Cloud Module, take a look at the API Reference.

Moment

Moment.js is a small JavaScript date library for parsing, validating, manipulating, and formatting dates. You can learn more about Moment on their website. To use it, you simply need to require it.

var moment = require('moment');

For additional information about the Moment.js Cloud Module, take a look at their API reference.

Note that the version of Moment.js that Parse offers is 1.7.2. If you require a newer version, download moment.js to your cloud/ folder and require it like this:

var moment = require('cloud/moment');

Parse Image

Images are the most common kind of Parse.File. The parse-image module provides an Image class that makes working with images in Cloud Code easier.

Reading Images from Files

To get started, create an Image using a Buffer with image file data. Usually, this data comes from a Parse.File. To read in the data from a file and create the image object, you can use our networking functions.

var Image = require("parse-image");

Parse.Cloud.httpRequest({
  url: object.get("profilePhoto").url(),
  success: function(response) {
    // The file contents are in response.buffer.
    var image = new Image();
    return image.setData(response.buffer, {
      success: function() {
        console.log("Image is " + image.width() + "x" + image.height() + ".");
      },
      error: function(error) {
        // The image data was invalid.
      }
    })
  },
  error: function(error) {
    // The networking request failed.
  }
});

Cropping Images

To extract a particular rectangle of an image, use the crop method. This lets you specify the area of the image you want to keep.

// Crop the image to the rectangle from (10, 10) to (30, 20).
image.crop({
  left: 10,
  top: 10,
  right: 30,
  bottom: 20,
  success: function(image) {
    // The image was cropped.
  },
  error: function(error) {
    // The image could not be cropped.
  }
});

Alternatively, you can supply a width and height instead of right and bottom.

// Crop the image to the rectangle from (10, 10) to (30, 20).
image.crop({
  left: 10,
  top: 10,
  width: 20,
  height: 10,
  success: function(image) {
    // The image was cropped.
  },
  error: function(error) {
    // The image could not be cropped.
  }
});

Scaling Images

You can also resize an image, scaling the graphic using interpolation. Images can be made either smaller or larger. Just specify the new width and height. If you leave either off, it will assume the current image size.

// Resize the image to 64x64.
image.scale({
  width: 64,
  height: 64,
  success: function(image) {
    // The image was scaled.
  },
  error: function(error) {
    // The image could not be scaled.
  }
});

Sometimes it's more convenient to specify a ratio to resize to, instead of absolute dimensions.

// Resize the image to 25% of its original size.
image.scale({
  ratio: 0.25,
  success: function(image) {
    // The image was scaled.
  },
  error: function(error) {
    // The image could not be scaled.
  }
});

Changing Image Formats

You can even change the file format of an image file. For example, you may want to convert a file to a JPEG to reduce file size and bandwidth usage.

// Change the image to be a JPEG.
image.setFormat("JPEG", {
  success: function(image) {
    // The image was changed to a JPEG.
  },
  error: function(error) {
    // The image could not be reformatted.
  }
});

Image Thumbnail Example

Every Image method returns a Parse.Promise in addition to having callbacks. This makes it easy to chain together multiple operations. Consider the common case where a user supplies an image to use for their profile. It's nice to be able to automatically generate a thumbnail of that photo to use in some of your UI. This can be done using a beforeSave handler.

var Image = require("parse-image");

Parse.Cloud.beforeSave("_User", function(request, response) {
  var user = request.object;
  if (!user.get("profilePhoto")) {
    response.error("Users must have a profile photo.");
    return;
  }

  if (!user.dirty("profilePhoto")) {
    // The profile photo isn't being modified.
    response.success();
    return;
  }

  Parse.Cloud.httpRequest({
    url: user.get("profilePhoto").url()

  }).then(function(response) {
    var image = new Image();
    return image.setData(response.buffer);

  }).then(function(image) {
    // Crop the image to the smaller of width or height.
    var size = Math.min(image.width(), image.height());
    return image.crop({
      left: (image.width() - size) / 2,
      top: (image.height() - size) / 2,
      width: size,
      height: size
    });

  }).then(function(image) {
    // Resize the image to 64x64.
    return image.scale({
      width: 64,
      height: 64
    });

  }).then(function(image) {
    // Make sure it's a JPEG to save disk space and bandwidth.
    return image.setFormat("JPEG");

  }).then(function(image) {
    // Get the image data in a Buffer.
    return image.data();

  }).then(function(buffer) {
    // Save the image into a new file.
    var base64 = buffer.toString("base64");
    var cropped = new Parse.File("thumbnail.jpg", { base64: base64 });
    return cropped.save();

  }).then(function(cropped) {
    // Attach the image file to the original object.
    user.set("profilePhotoThumbnail", cropped);

  }).then(function(result) {
    response.success();
  }, function(error) {
    response.error(error);
  });
});

SendGrid

SendGrid is a cloud-based email service that delivers email on behalf of companies to increase deliverability and improve customer communications. If you do not already have a SendGrid account, you can do so (here)[http://www.sendgrid.com].

SendGrid provides reliable delivery, scalability and real-time analytics along with flexible APIs that make custom integration simple. Access advanced metrics and reporting with our powerful APIs to customize, measure and automate your email program.

Sending Email

The SendGrid module allows you to send attachments to multiple recipients and all the features of the SMTPAPI Header. For a more comprehensive documentation and examples, please visit the official repository for this library.

var sendgrid = require("sendgrid");

sendgrid.initialize("sendgrid_username", "sendgrid_password");
SendGrid.sendEmail({
  to: ["email@example.com (mailto:email@example.com)", "email+1@example.com"],
  from: "SendGrid@CloudCode.com (mailto:SendGrid@CloudCode.com)",
  subject: "Hello from Cloud Code!",
  text: "Using Parse and SendGrid is great!",
  replyto: "reply@example.com (mailto:reply@example.com)"
}).then(function(httpResponse) {
  console.log(httpResponse);
  response.success("Email sent!");
},function(httpResponse) {
  console.error(httpResponse);
  response.error("Uh oh, something went wrong");
});

Email Webhooks

SendGrid offers two webhooks, one for events and another for incoming email. Here are some cool things you can do with these webhooks.

With the Event Webhook, SendGrid allows you to monitor all stats with your SendGrid account in real-time and drill down to individual recipients to see who is opening and clicking your important messages. The Event Webhook will notify a URL of your choice via HTTP POST with information about these events as SendGrid processes your email. Common uses of this data are to remove unsubscribes, react to spam reports, determine unengaged recipients, identify bounced email addresses or create advanced analytics of your email program.

Enabling the SendGrid Event Webhook requires simply adding the endpoint in your Parse app to your settings:

The SendGrid Parse Webhook allows you to manage inbound email with as a smarter alternative to no-reply addresses. SendGrid can parse the attachments and contents of incoming emails enabling users to post content to an app (blog entries, photo uploads, etc.) via email. The SendGrid Parse API will POST the parsed email to a URL that you specify.

You can enable the SendGrid Parse Webhook by adding the following settings to your account:

Stripe

Stripe provides a very easy-to-use API for processing credit cards on the web or in your mobile app. You can take a look at their service at www.stripe.com. To use this Cloud Module, you will need to head over to the Stripe website and create an account. The current version of the Stripe Cloud Module supports the majority of their REST API.

To use this module in your Cloud Code functions, start by requiring and initializing it with your credentials.

var Stripe = require('stripe');
Stripe.initialize('mySecretKey');

Charging a Credit Card

Charging a credit card using Stripe and Parse will usually follow this flow.

  1. Getting a Credit Card Token

The first step to charging a credit card is to generate a token using the Stripe API. You should do this from your mobile or web client using your "publishable key". This will ensure that only Stripe manipulates the sensitive credit card information. If you are using iOS, you can use the Stripe iOS SDK, otherwise take a look at the Creating a Token section of their REST API documentation.

  1. Calling your Cloud Code Function

After successfully creating a token, you can send the card token to a Cloud Code function. For more on creating and calling Parse Cloud Functions from the client SDKs or REST API, take a look at the Cloud Code Guide.

  1. Charging the Credit Card

From a Cloud Code function, you can then use the Stripe Cloud Module to charge the credit card token. Using the Stripe.Charges.create function you can specify the amount, currency, and card token to use for the purchase. The full list of available parameters can be found in Stripe's API documentation, but the following example demonstrates the basic use case.

Stripe.Charges.create({
  amount: 100 * 10, // $10 expressed in cents
  currency: "usd",
  card: "tok_3TnIVhEv9P24T0" // the token id should be sent from the client
},{
  success: function(httpResponse) {
    response.success("Purchase made!");
  },
  error: function(httpResponse) {
    response.error("Uh oh, something went wrong");
  }
});

Available Functionality

There are many more functions available from the Stripe Cloud Module. Please consult the Stripe Cloud Module API reference for the full list of available functions and consult Stripe's own REST documentation for more information on how their platform works.

Twilio

Parse provides the full functionality of the Twilio-node module including SMS, Voice, and Twiml features. Please refer to the documentation here: Twilio-node module documentation. You do not need to install the module, as it is already installed and hosted on Parse.

Usage Example

// Require and initialize the Twilio module with your credentials
var client = require('twilio')('ACCOUNT_SID', 'AUTH_TOKEN');

// Send an SMS message
client.sendSms({
    to:'+16515556677', 
    from: '+14506667788', 
    body: 'Hello world!' 
  }, function(err, responseData) { 
    if (err) {
      console.log(err);
    } else { 
      console.log(responseData.from); 
      console.log(responseData.body);
    }
  }
);

Inbound Requests

With Express in Cloud Code, you can define routes to handle incoming requests. The following is an example Custom Webhook that receives requests from Twilio.

var express = require('express');
var app = express();

// Global app configuration section
app.use(express.bodyParser());  // Populate req.body

app.post('/receiveSMS',
         function(req, res) {

  console.log("Received a new text: " + req.body.From);
  res.send('Success');
});

app.listen();

After creating the route and deploying, configure the URL with Twilio and your webhook will begin handling requests from Twilio. You will have access to all of the request data through req.body in your Custom Webhook. The full list of available data can be found in the Twilio documentation.

For additional information about the Twilio Cloud Module, take a look at the twilio-node Reference.

Underscore

Underscore.js is a utility-belt library for JavaScript that provides a lot of the functional programming support. You can learn more about Underscore on their website. The Parse SDK uses this library internally but it is also available in Cloud Code using the underscore Cloud Module. To use it, you simply need to require it.

var _ = require('underscore');

For additional information about the Underscore.js Cloud Module, take a look at their API reference

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 your 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 and Linux/Unix environments, you can get the parse 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. Note that this is not an installer, it is just a plain windows executable. The downloaded executable is named like: parse-windows-x.xx.xx.exe. You can rename it to parse.exe and move it to an accessible location, so that you can run it by typing parse at your command prompt.

Updating

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

$ parse update
Fetching latest version ...
####################################################################### 100%
Installing ...

Setting Up Your App

The next step is to create a directory to store the code that you will run in the cloud. The command parse new sets up this directory, and will prompt you to pick which app you are creating Cloud Code for:

$ parse new MyCloudCode
Email: ninja@gmail.com
Password:
1:MyApp
Select an App: 1
$ cd MyCloudCode

Use the email address and password for your Parse account to log in. If you signed up via OAuth and never set a password, you should now set one by editing your account settings. This will create a directory called MyCloudCode in the current directory. Several files are automatically created for you:

.
├── cloud
│   └── main.js
├── config
│   └── global.json
└── public
    └── index.html

The config directory contains a JSON configuration file that you shouldn't normally need to deal with, 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. Keep in mind that these files will contain keys you want to keep private.

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 for new take an optional application 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 v1 (using Parse JavaScript SDK vx.x.x)

This pushes the new code (in cloud/main.js) 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 like so:

$ 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 v2 (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.

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, as well as providing a live stream of the logs.

$ parse develop development
E2013-03-19:20:17:01.423Z] 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
New release is named v58
I2013-03-19T20:17:10.343Z] Deployed v58 with triggers:
  GameScore:
    before_save

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 application as a target by running the add command. This prompts you for your Parse.com email and password and provides you a list of applications to choose from:

$ parse add
Email: pirate@gmail.com
Password(will be hidden):
1:PiecesOfEightCounter
2:BootyDivider
Select an App: 1

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.

Typically, all of this configuration data gets stored in the global.json. However, you might have an app that you use for development that you do not want to share with the rest of your team. You can use the --local flag to add this configuration instead to a separate local.json file. This way, you can check global.json into source control, while keeping local.json just on your own machine.

Setting the Default App

parse deploy, parse log, 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 log command. There are two types of logs:

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

$ parse log -n 1
I2012-07-10:13:37:00] 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-03-11T18:17:52Z
v15                             No release notes given          2015-03-11T18:45:32Z
v16                             Moved to webhooks               2015-03-16T21:32:02Z
...

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 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.2.13
$ parse jssdk -a
  1.2.18
  1.2.17
  1.2.16
  1.2.15
  1.2.14
* 1.2.13
  1.2.12
  1.2.11
$ parse jssdk 1.2.18
Current JavaScript SDK version is 1.2.18
$ parse jssdk -a
* 1.2.18
  1.2.17
  1.2.16
  1.2.15
  1.2.14
  1.2.13
  1.2.12
  1.2.11
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.

Performance

As your app scales, you will want to ensure that it performs well under increased load and usage. There are parts of optimizing performance that Parse takes care of but there are some things you can do. This document provides guidelines on how you can optimize your app's performance. While you can use Parse for quick prototyping and not worry about performance, you will want to keep our performance guidelines in mind when you're initially designing your app. We strongly advise that you make sure you've followed all suggestions before releasing your app.

Parse provides services out of the box to help your app scale auto-magically. On top of our MongoDB datastore, we have built an API layer that seamlessly integrates with our client-side SDKs. Our cloud infrastructure uses online learning algorithms to automatically rewrite inefficient queries and generate database indexes based on your app’s realtime query stream.

In addition to what Parse provides, you can improve your app's performance by looking at the following:

Keep in mind that not all suggestions may apply to your app. Let's look into each one of these in more detail.

Write Efficient Queries

Parse objects are stored in a database. A Parse query retrieves objects that you are interested in based on conditions you apply to the query. To avoid looking through all the data present in a particular Parse class for every query, the database can use an index. An index is a sorted list of items matching a given criteria. Indexes help because they allow the database to do an efficient search and return matching results without looking at all of the data. Indexes are typically smaller in size and available in memory, resulting in faster lookups.

Smart Indexing

The key to writing efficient queries is understanding our indexing strategy. If your data is not indexed, every query will have to go through the the entire data for a class to return a query result. On the other hand, if your data is indexed appropriately, the number of documents scanned to return a correct query result should be low.

One of the advantages to using Parse if that you don't have to worry about managing your own database and maintaining indexes. We've built an abstraction to manage all that complexity. However, you do have to organize your data model and use performant queries to take advantage of this. To better understand how to go about doing this, you need to understand how our systems are operating behind the abstraction. The key strategy you will want to understand here is our use of smart indexing.

Smart indexing means that we algorithmically generate indexes for the apps that we host. The sheer number of apps hosted on Parse means that we cannot manually generate indexes for each app. This would not scale well as developers can change their schemas or query patterns at any time. This is why we rely on smart indexes.

We perform two types of index creation logic. The first generates simple (single field) indexes for each API request, and the second does offline processing to pick good compound indexes based on real API traffic patterns. In each case the goal is to pick indexes that result in the smallest search space for the query, that is, there will be less data to scan to find results.

The simple indexing strategy looks at every API request and attempts to pick good indexes based on the following:

The order of a query constraint's usefulness is:

Take a look at the following query to retrieve GameScore objects:

var GameScore = Parse.Object.extend("GameScore");
var query = new Parse.Query(GameScore);
query.equalTo("score", 50);
query.containedIn("playerName",
    ["Jonathan Walsh", "Dario Wunsch", "Shawn Simon"]);
PFQuery *query = [PFQuery queryWithClassName:@"GameScore"];
[query whereKey:@"score" equalTo:@50];
[query whereKey:@"playerName"
    containedIn:@[@"Jonathan Walsh", @"Dario Wunsch", @"Shawn Simon"]];
let query = PFQuery.queryWithClassName("GameScore")
query.whereKey("score", equalTo: 50)
query.whereKey("playerName", containedIn: ["Jonathan Walsh", "Dario Wunsch", "Shawn Simon"])

Creating an index query based on the score field would yield a smaller search space in general than creating one on the playerName field.

When examining data types, booleans have a very low entropy and and do not make good indexes. Take the following query constraint:

query.equalTo("cheatMode", false);
[query whereKey:@"cheatMode" equalTo:@NO];
query.whereKey("cheatMode", equalTo: false)

The two possible values for cheatMode are true and false. If an index was added on this field it would be of little use because it's likely that 50% of the records will have to be looked at to return query results.

We also throw out relations and join tables, since no values are stored for these keys. We heavily promote GeoPoints since MongoDB won’t run a geo query without a geo index. Other data types are ranked by their expected entropy of the value space for the key:

We score each query according to the above metrics, and make sure we create a unique index on the three top-scoring fields for each query. For a compound query that consists of an OR of subqueries, we compute the top three indexes for each subquery.

Even the best indexing strategy can be defeated by suboptimal queries. You will need to design queries that work hand in hand with smart indexing to deliver performant apps.

Efficient Query Design

Writing efficient queries means taking full advantage of indexes. Let's take a look at some query constraints that negate the use of indexes:

Additionally, the following queries under certain scenarios may result in slow query responses if they can't take advantage of indexes:

Not Equal To

For example, let's say you're tracking high scores for a game in a GameScore class. Now say you want to retrieve the scores for all players except a certain one. You could create this query:

var GameScore = Parse.Object.extend("GameScore");
var query = new Parse.Query(GameScore);
query.notEqualTo("playerName", "Michael Yabuti");
query.find().then(function(results) {
  // Retrieved scores successfully
});
PFQuery *query = [PFQuery queryWithClassName:@"GameScore"];
[query whereKey:@"playerName" notEqualTo:@"Michael Yabuti"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
  if (!error) {
    // Retrieved scores successfully
  }
}];
let query = PFQuery.queryWithClassName("GameScore")
query.whereKey("playerName", notEqualTo: "Michael Yabuti")
query.findObjectsInBackgroundWithBlock {
  (objects, error) in
  if !error {
    // Retrieved scores successfully
  }
}

This query can't take advantage of indexes. The database has to look at all the objects in the GameScore class to satisfy the constraint and retrieve the results. As the number of entries in the class grows, the query takes longer to run.

Luckily, most of the time a “Not Equal To” query condition can be rewritten as a “Contained In” condition. Instead of querying for the absence of values, you ask for values which match the rest of the column values. Doing this allows the database to use an index and your queries will be faster.

For example if the User class has a column called state which has values “SignedUp”, “Verified”, and “Invited”, the slow way to find all users who have used the app at least once would be to run the query:

var query = new Parse.Query(Parse.User);
query.notEqualTo("state", "Invited");
PFQuery *query = [PFUser query];
[query whereKey:@"state" notEqualTo:@"Invited"];
var query = PFUser.query()
query.whereKey("state", notEqualTo: "Invited")

It would be faster to use the “Contained In” condition when setting up the query:

query.containedIn("state", ["SignedUp", "Verified"]);
[query whereKey:@"state"
    containedIn:@[@"SignedUp", @"Verified"]];
query.whereKey("state", containedIn: ["SignedUp", "Verified"])

Sometimes, you may have to completely rewrite your query. Going back to the GameScore example, let's say we were running that query to display players who had scored higher than the given player. We could do this differently, by first getting the given player's high score and then using the following query:

var GameScore = Parse.Object.extend("GameScore");
var query = new Parse.Query(GameScore);
// Previously retrieved highScore for Michael Yabuti
query.greaterThan("score", highScore);
query.find().then(function(results) {
  // Retrieved scores successfully
});
PFQuery *query = [PFQuery queryWithClassName:@"GameScore"];
// Previously retrieved highScore for Michael Yabuti
[query whereKey:@"score" greaterThan:highScore];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
  if (!error) {
    // Retrieved scores successfully
  }
}];
let query = PFQuery.queryWithClassName("GameScore")
// Previously retrieved highScore for Michael Yabuti
query.whereKey("score", greaterThan: highScore)
query.findObjectsInBackgroundWithBlock {
  (objects, error) in
  if !error {
    // Retrieved scores successfully
  }
}

The new query you use depends on your use case. This may sometimes mean a redesign of your data model.

Not Contained In

Similar to “Not Equal To”, the “Not Contained In” query constraint can't use an index. You should try and use the complementary “Contained In” constraint. Building on the User example, if the state column had one more value, “Blocked”, to represent blocked users, a slow query to find active users would be:

var query = new Parse.Query(Parse.User);
query.notContainedIn("state", ["Invited", "Blocked"];
PFQuery *query = [PFUser query];
[query whereKey:@"state" notContainedIn:@[@"Invited", @"Blocked"]];
var query = PFUser.query()
query.whereKey("state", containedIn: ["Invited", "Blocked"])

Using a complimentary “Contained In” query constraint will always be faster:

query.containedIn("state", ["SignedUp", "Verified"]);
query whereKey:@"state" containedIn:@[@"SignedUp", @"Verified"]];
query.whereKey("state", containedIn: ["SignedUp", "Verified"])

This means rewriting your queries accordingly. Your query rewrites will depend on your schema set up. It may mean redoing that schema.

Regular Expressions

Most regular expression queries in Parse are heavily throttled due to performance considerations. MongoDB is not efficient for doing partial string matching except for the special case where you only want a prefix match. Queries that have regular expression constraints are therefore very expensive, especially for classes with over 100,000 records. Parse restricts how many such operations can be run on a particular app at any given time.

You should avoid using regular expression constraints that don't use indexes. For example, the following query looks for data with a given string in the playerName field. The string search is case insensitive:

query.matches("playerName", "Michael", “i”);
[query whereKey:@"playerName" matchesRegex:@"Michael" modifiers:@"i"];
query.whereKey("playerName", matchesRegex: "Michael", modifiers: "i")

A similar query looks for any occurrence of the string in the field, however the search is case sensitive:

query.contains("playerName", "Michael");
[query whereKey:@"playerName" containsString:@"Michael"];
query.whereKey("playerName", containsString: "Michael")

These queries are both slow. Depending on your use case, you should switch to using the following constraint that uses an index:

query.startsWith("playerName", "Michael");
[query whereKey:@"playerName" hasPrefix:@"Michael"];
query.whereKey("playerName", hasPrefix: "Michael")

This looks for data that starts with the given string. This query will use the backend index, so it will be faster even for large datasets.

As a best practice, when you use regular expression constraints, you'll want to ensure that other constraints in the query reduce the result set to the order of hundreds of objects to make the query efficient. If you must use the matches()constraint for legacy reasons, then use case sensitive, anchored queries where possible, for example:

query.matches("playerName", "^Michael");
[query whereKey:@"playerName" matchesRegex:@"^Michael"];
query.whereKey("playerName", matchesRegex: "^Michael")

Most of the use cases around using regular expressions involve implementing search. A more performant way of implementing search is detailed later.

Write Restrictive Queries

Writing restrictive queries allows you to return only the data that the client needs. This is critical in a mobile environment were data usage can be limited and network connectivity unreliable. You also want your mobile app to appear responsive and this is directly affected by the objects you send back to the client. The Querying Guide shows the types of constraints you can add to your existing queries to limit the data returned. When adding constraints, you want to pay attention and design efficient queries.

You can limit the number of query results returned. The limit is 100 by default but anything from 1 to 1000 is a valid limit:

query.limit(10); // limit to at most 10 results
query.limit = 10; // limit to at most 10 results
query.limit = 10 // limit to at most 10 results

If you're issuing queries on GeoPoints, make sure you specify a reasonable radius:

var query = new Parse.Query(PlaceObject);
query.withinMiles("location", userGeoPoint, 10.0);
query.find().then(function(placesObjects) {
  // Get a list of objects within 10 miles of a user's location
});
PFQuery *query = [PFQuery queryWithClassName:@"Place"];
[query whereKey:@"location" nearGeoPoint:userGeoPoint withinMiles:10.0];
[query findObjectsInBackgroundWithBlock:^(NSArray *places, NSError *error) {
  if (!error) {
    // List of objects within 10 miles of a user's location
  }
}];
let query = PFQuery.queryWithClassName("Place")
query.whereKey("location", nearGeoPoint: userGeoPoint, withinMiles: 10.0)
query.findObjectsInBackgroundWithBlock {
  (places, error) in
  if !error {
    // List of places within 10 miles of a user's location
  }
}

You can further limit the fields returned by calling select:

var GameScore = Parse.Object.extend("GameScore");
var query = new Parse.Query(GameScore);
query.select("score", "playerName");
query.find().then(function(results) {
  // each of results will only have the selected fields available.
});
PFQuery *query = [PFQuery queryWithClassName:@"GameScore"];
[query selectKeys:@[@"score", @"playerName"]];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
  if (!error) {
    // each of results will only have the selected fields available.
  }
}];
let query = PFQuery.queryWithClassName("GameScore")
query.selectKeys(["score", "playerName"])
query.findObjectsInBackgroundWithBlock {
  (objects, error) in
  if !error {
    // each of results will only have the selected fields available.
  }
}

Client-side Caching

For queries run from iOS and Android, you can turn on query caching. See the iOS and Android guides for more details. Caching queries will increase your mobile app's performance especially in cases where you want to display cached data while fetching the latest data from Parse.

Use Cloud Code

Cloud Code allows you to run custom JavaScript logic on Parse instead of on the client.

You can use this to off load processing to the Parse servers thus increasing your app's perceived performance. You can create hooks that run whenever an object is saved or deleted. This is useful if you want to validate or sanitize your data. You can also use Cloud Code to modify related objects or kick off other processes such as sending off a push notification. There are time limits to how long Cloud Code can run, for example an afterSave hook only has 3 seconds to run. You can use Background Jobs if you need to run more time consuming processes such as data migrations.

We saw examples of limiting the data returned by writing restrictive queries. You can also use Cloud Functions to help limit the amount of data returned to your app. In the following example, we use a Cloud Function to get a movie's average rating:

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

You could have ran a query on the Review class on the client, returned only the stars field data and computed the result on the client. As the number of reviews for a movie increases you can see that the data being returned to the device using this methodology also increases. Implementing the functionality through a Cloud Function returns the one result if successful.

As you look at optimizing your queries, you'll find that you may have to change the queries - sometimes even after you've shipped your app to the App Store or Google Play. The ability to change your queries without a client update is possible if you use Cloud Functions. Even if you have to redesign your schema, you could make all the changes in your Cloud Functions while keeping the client interface the same to avoid an app update. Take the average ratings Cloud Function example from before, calling it from a client SDK would look like this:

[PFCloud callFunctionInBackground:@"averageStars"
                  withParameters:@{@"movie": @"The Matrix"}
                           block:^(NSNumber *ratings, NSError *error) {
  if (!error) {
    // ratings is 4.5
  }
}];
PFCloud.callFunctionInBackground("averageStars", withParameters: ["movie": "The Matrix"]) {
  (ratings, error) in
  if !error {
    // ratings is 4.5
  }
}

If later on, you need to modify the underlying data model, your client call can remain the same, as long as you return back a number that represents the ratings result.

Avoid Count Operations

For classes with over 1,000 objects, count operations are limited by timeouts. They may routinely yield timeout errors or return results that are only approximately correct. Thus, it is preferable to architect your application to avoid this count operation.

Suppose you are displaying movie information in your app and your data model consists of a Movie class and a Review class that contains a pointer to the corresponding movie. You might want to display the review count for each movie on the top-level navigation screen using a query like this:

var Review = Parse.Object.extend("Review");
var query = new Parse.Query("Review");
// movieId corresponds to a given movie's id
query.equalTo(“movie”, movieId);
query.count().then(function(count) {
  // Request succeeded
});
PFQuery *query = [PFQuery queryWithClassName:@"Review"];
// movieId corresponds to a given movie's id
[query whereKey:@"movie" equalTo:movieId];
[query countObjectsInBackgroundWithBlock:^(int number, NSError *error) {
  if (!error) {
    // Request succeeded
  }
}];
let query = PFQuery.queryWithClassName("Review")
// movieId corresponds to a given movie's id
query.whereKey("movie", equalTo: movieId)
query.countObjectsInBackgroundWithBlock {
  (number, error) in
  if !error {
    // Request succeeded
  }
}

If you run the count query for each of the UI elements, they will not run efficiently on large data sets. One approach to avoid using the count() operator could be to add a field to the Movie class that represents the review count for that movie. When saving an entry to the Review class you could increment the corresponding movie's review count field. This can be done in an afterSave handler:

Parse.Cloud.afterSave("Review", function(request) {
  // Get the movie id for the Review
  var movieId = request.object.get("movie").id;
  // Query the Movie represented by this review
  var Movie = Parse.Object.extend("Movie");
  var query = new Parse.Query(Movie);
  query.get(movieId).then(function(movie) {
    // Increment the reviews field on the Movie object
    movie.increment("reviews");
    movie.save();
  }, function(error) {
    throw "Got an error " + error.code + " : " + error.message;
  });
});

Your new optimized query would not need to look at the Review class to get the review count:

var Movie = Parse.Object.extend("Movie");
var query = new Parse.Query(Movie);
query.find().then(function(results) {
  // Results include the reviews count field
}, function(error) {
  // Request failed
});
PFQuery *query = [PFQuery queryWithClassName:@"Movie"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
  if (!error) {
    // Results include the reviews count field
  }
}];
let query = PFQuery.queryWithClassName("Movie")
query.findObjectsInBackgroundWithBlock {
  (objects, error) in
  if !error {
    // Results include the reviews count field
  }
}

You could also use a separate Parse Object to keep track of counts for each review. Whenever a review gets added or deleted, you can increment or decrement the counts in an afterSave or afterDelete Cloud Code handler. The approach you choose depends on your use case.

Implement Efficient Searches

As mentioned previously, MongoDB is not efficient for doing partial string matching. However, this is an important use case when implementing search functionality that scales well in production.

Simplistic search algorithms simply scan through all the class data and executes the query on each entry. The key to making searches run efficiently is to minimize the number of data that has to be examined when executing each query by using an index as we've outlined earlier. You’ll need to build your data model in a way that it’s easy for us to build an index for the data you want to be searchable. For example, string matching queries that don’t match an exact prefix of the string won’t be able to use an index leading to timeout errors as the data set grows.

Let's walk through an example of how you could build an efficient search. You can apply the concepts you learn in this example to your use case. Say your app has users making posts, and you want to be able to search those posts for hashtags or particular keywords. You’ll want to pre-process your posts and save the list of hashtags and words into array fields. You can do this processing either in your app before saving the posts, or you can use a Cloud Code beforeSave hook to do this on the fly:

var _ = require("underscore");
Parse.Cloud.beforeSave("Post", function(request, response) {
  var post = request.object;
  var toLowerCase = function(w) { return w.toLowerCase(); };
  var words = post.get("text").split(/\b/);
  words = _.map(words, toLowerCase);
  var stopWords = ["the", "in", "and"]
  words = _.filter(words, function(w) {
    return w.match(/^\w+$/) && !   _.contains(stopWords, w);
  });
  var hashtags = post.get("text").match(/#.+?\b/g);
  hashtags = _.map(hashtags, toLowerCase);
  post.set("words", words);
  post.set("hashtags", hashtags);
  response.success();
});

This saves your words and hashtags in array fields, which MongoDB will store with a multi-key index. There are some important things to notice about this. First of all it’s converting all words to lower case so that we can look them up with lower case queries, and get case insensitive matching. Secondly, it’s filtering out common words like ‘the’, ‘in’, and ‘and’ which will occur in a lot of posts, to additionally reduce useless scanning of the index when executing the queries.

Once you've got the keywords set up, you can efficiently look them up using “All” constraint on your query:

var Post = Parse.Object.extend("Post");
var query = new Parse.Query(Post);
query.containsAll("hashtags", [“#parse”, “#ftw”]);
query.find().then(function(results) {
  // Request succeeded
}, function(error) {
  // Request failed
});
PFQuery *query = [PFQuery queryWithClassName:@"Post"];
[query whereKey:@"hashtags" containsAllObjectsInArray:@[@"#parse", @"#ftw"]];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
  if (!error) {
    // Request succeeded
  }
}];
let query = PFQuery.queryWithClassName("Post")
query.whereKey("hashtags", containsAllObjectsInArray: ["#parse", "#ftw"])
query.findObjectsInBackgroundWithBlock {
  (objects, error) in
  if !error {
    // Request succeeded
  }
}

Limits and Other Considerations

There are some limits in place to ensure the API can provide the data you need in a performant manner. We may adjust these in the future. Please take a moment to read through the following list:

Objects

Files

Queries

Cloud Code

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.

Error Codes

The following is a comprehensive list of all the error codes that can be returned by the Parse API.

Name Code Description
OtherCause -1 An unknown error or an error unrelated to Parse occurred.
InternalServerError 1 Internal server error. No information available.
ConnectionFailed 100 The connection to the Parse servers failed.
ObjectNotFound 101 The specified object doesn't exist.
InvalidQuery 102 You tried to find values matching a datatype that doesn't support exact database matching, like an array or a dictionary.
InvalidClassName 103 Missing or invalid classname. Classnames are case-sensitive. They must start with a letter, and a-zA-Z0-9_ are the only valid characters.
MissingObjectId 104 An unspecified object id.
InvalidKeyName 105 An invalid key name. Keys are case-sensitive. They must start with a letter, and a-zA-Z0-9_ are the only valid characters.
InvalidPointer 106 A malformed pointer. You should not see this unless you have been mucking about changing internal Parse code.
InvalidJSON 107 Badly formed JSON was received upstream. This either indicates you have done something unusual with modifying how things encode to JSON, or the network is failing badly.
CommandUnavailable 108 The feature you tried to access is only available internally for testing purposes.
NotInitialized 109 You must call Parse.initialize before using the Parse library. Check the Quick Start guide for your platform.
IncorrectType 111 A field was set to an inconsistent type.
InvalidChannelName 112 Invalid channel name. A channel name is either an empty string (the broadcast channel) or contains only a-zA-Z0-9_ characters and starts with a letter.
InvalidSubscriptionType 113 Bad subscription type.
InvalidDeviceToken 114 The provided device token is invalid.
PushMisconfigured 115 Push is misconfigured in your app. See error details to find out how.
ObjectTooLarge 116 The object is too large. Parse.Objects have a max size of 128 kilobytes.
InvalidLimitError 117 An invalid value was set for the limit. A limit must be a non-negative integer.
InvalidSkipError 118 An invalid value was set for skip.
OperationForbidden 119 The operation isn't allowed for clients.
CacheMiss 120 The result was not found in the cache.
InvalidNestedKey 121 An invalid key was used in a nested JSONObject.
InvalidFileName 122 An invalid filename was used for Parse.File. A valid file name contains only a-zA-Z0-9_. characters and is between 1 and 128 characters.
InvalidACL 123 An invalid ACL was provided.
Timeout 124 The request timed out on the server. Typically this indicates that the request is too expensive to run.
InvalidEmailAddress 125 The email address was invalid.
MissingContentType 126 Missing content type.
MissingContentLength 127 Missing content length.
InvalidContentLength 128 Invalid content length.
FileTooLarge 129 File that was too large. Files are limited to 10 MB.
FileSaveError 130 Error saving a file.
FileDeleteError 131 File could not be deleted.
InvalidInstallationIdError 132 Invalid installation id.
InvalidDeviceTypeError 133 Invalid device type.
InvalidChannelsArrayError 134 Invalid channels array value.
MissingRequiredFieldError 135 Required field is missing.
ChangedImmutableFieldError 136 An immutable field was changed.
DuplicateValue 137 Unique field was given a value that is already taken.
InvalidExpirationError 138 Invalid expiration value.
InvalidRoleName 139 Role's name is invalid.
ExceededQuota 140 An application quota was exceeded. Upgrade to resolve.
ScriptFailed 141 Cloud Code script failed. Usually points to a JavaScript error in your script.
ValidationFailed 142 Cloud Code validation failed.
ReceiptMissing 143 Product purchase receipt is missing.
InvalidPurchaseReceipt 144 Product purchase receipt is invalid.
PaymentDisabled 145 Payment is disabled on this device.
InvalidProductIdentifier 146 The product identifier is invalid.
ProductNotFoundInAppStore 147 The product is not found in the App Store.
InvalidServerResponse 148 The Apple server response is not valid.
ProductDownloadFilesystemError 149 The product fails to download due to file system error.
InvalidImageData 150 Invalid image data.
UnsavedFileError 151 An unsaved file.
InvalidPushTimeError 152 An invalid push time.
FileDeleteFailed 153 A file deletion failed.
InefficientQueryError 154 An inefficient query was rejected by the server.
RequestLimitExceeded 155 An application has exceeded its request limit. Upgrade to resolve.
MissingPushIdError 156 A push id is missing.
MissingDeviceTypeError 157 The device type field is missing.
TemporaryRejectionError 159 An application's requests are temporary rejected by the server. File a bug report for further instructions.
InvalidEventName 160 The provided event name is invalid.
UsernameMissing 200 The username is missing or empty.
PasswordMissing 201 The password is missing or empty.
UsernameTaken 202 The username has already been taken.
UserEmailTaken 203 Email has already been taken.
UserEmailMissing 204 The email is missing, and must be specified.
UserWithEmailNotFound 205 A user with the specified email was not found.
SessionMissing 206 A user object without a valid session could not be altered.
MustCreateUserThroughSignup 207 A user can only be created through signup.
AccountAlreadyLinked 208 An account being linked is already linked to another user.
InvalidSessionToken 209 The device's session token is no longer valid. The developer should ask the user to log in again.
LinkedIdMissing 250 A user cannot be linked to an account because that account's id could not be found.
InvalidLinkedSession 251 A user with a linked (e.g. Facebook or Twitter) account has an invalid session.
UnsupportedService 252 A service being linked (e.g. Facebook or Twitter) is unsupported.
InvalidAuthDataError 253 An invalid authData value was passed. It must be a Hash, not String.
AggregateError 600 There were multiple errors. Aggregate errors have an "errors" property, which is an array of error objects with more detail about each error that occurred.
XDomainRequest A real error code is unavailable because we had to use an XDomainRequest object to allow CORS requests in Internet Explorer, which strips the body from HTTP responses that have a non-2XX status code.
Unauthorized Unauthorized request. Are you missing an authentication header?
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.