authData
authData
authData
authData
authData
authData
authData
authData
authData
authData
authData
authData
authData
authData
authData
authData
authData
Parse Server is an open source backend that can be deployed to any infrastructure that can run Node.js. You can find the source on the GitHub repo.
The following guide describes how to set up Parse Server on your personal computer for local development. If you want to be able to access Parse Server from anywhere and make your app accessible publicly, you would want to deploy Parse Server to a cloud service provider like Amazon Web Services, Google Cloud, Microsoft Azure, Heroku or DigitalOcean. These providers vary in set-up complexity, configuration efforts, pricing model and required knowledge to secure your deployment. You can find guides for how to deploy Parse Server for specific providers in Deploying Parse Server section.
⚠️ Before making Parse Server accessible publicly, we strongly recommend to review all of your Parse Server configuration and read our best practice guide. Failing to properly adapt your Parse Server configuration for a publicly accessible environment may make your deployment vulnerable to malicious intrusions, data leaks and unexpected cost increases.
Prerequisites
Ensure that the Node.js version is compatible with your version of Parse Server, for details see the compatibility table.
The fastest and easiest way to get started is to run MongoDB and Parse Server locally. Use the bootstrap script to set up Parse Server in the current directory.
sh <(curl -fsSL https://raw.githubusercontent.com/parse-community/parse-server/master/bootstrap.sh)
npm install -g mongodb-runner
mongodb-runner start
npm start
You can use any arbitrary string as your application id and master key. These will be used by your clients to authenticate with the Parse Server.
That’s it! You are now running a standalone version of Parse Server on your machine.
Now that you’re running Parse Server, it is time to save your first object. We’ll use the REST API, but you can easily do the same using any of the Parse SDKs. Run the following:
curl -X POST \
-H "X-Parse-Application-Id: APPLICATION_ID" \
-H "Content-Type: application/json" \
-d '{"score":123,"playerName":"Sean Plott","cheatMode":false}' \
http://localhost:1337/parse/classes/GameScore
You should get a response similar to this:
{
"objectId": "2ntvSpRGIK",
"createdAt": "2022-01-01T12:23:45.678Z"
}
You can now retrieve this object directly (make sure to replace 2ntvSpRGIK
with the actual objectId
you received when the object was created):
curl -X GET \
-H "X-Parse-Application-Id: APPLICATION_ID" \
http://localhost:1337/parse/classes/GameScore/2ntvSpRGIK
// Response
{
"objectId": "2ntvSpRGIK",
"score": 123,
"playerName": "Sean Plott",
"cheatMode": false,
"updatedAt": "2022-01-01T12:23:45.678Z",
"createdAt": "2022-01-01T12:23:45.678Z"
}
Keeping tracks of individual object ids is not ideal, however. In most cases you will want to run a query over the collection, like so:
curl -X GET \
-H "X-Parse-Application-Id: APPLICATION_ID" \
http://localhost:1337/parse/classes/GameScore
// The response will provide all the matching objects within the `results` array:
{
"results": [
{
"objectId": "2ntvSpRGIK",
"score": 123,
"playerName": "Sean Plott",
"cheatMode": false,
"updatedAt": "2022-01-01T12:23:45.678Z",
"createdAt": "2022-01-01T12:23:45.678Z"
}
]
}
To learn more about using, saving, and querying objects on Parse Server, check out the documentation for the SDK you will be using in your app.
Parse provides SDKs for all the major platforms. Refer to the rest of the Parse Server guide to learn how to connect your app to Parse Server.
Once you have a better understanding of how the project works, please refer to the Deploying Parse Server section to learn more about additional ways of running Parse Server.
Parse Server lets you use MongoDB or Postgres as a database.
The prefered database is MongoDB but Postgres is a great option if you’re starting a new project and you expect to have a stable Schema.
If you have not used MongoDB before, we highly recommend familiarizing yourself with it first before proceeding.
If this is your first time setting up a MongoDB instance, we recommend a Database-as-a-Service (DBaaS) like MongoDB Atlas or ObjectRocket which provide fully managed MongoDB instances and can help you scale as needed.
Ensure that the MongoDB version is compatible with your version of Parse Server, for details see the compatibility table
When using MongoDB with your Parse app, you need to manage your indexes yourself. You will also need to size up your database as your data grows.
In order to allow for better scaling of your data layer, it is possible to direct queries to a MongoDB secondary for read operations. See: MongoDB Read Preference.
Ensure that the Postgres version is compatible with your version of Parse Server, for details see the compatibility table
PostGIS is required if you plan to use geographic or location features.
The Postgres database adapter will be automatically loaded when you pass a valid Postgres URL, for example: postgres://localhost:5432
. The available configuration options through the URL are:
postgres://localhost:5432/db?ssl=boolean&rejectUnauthorized=boolean&ca=/path/to/file&pfx=/path/to/file&cert=/path/to/file&key=/path/to/file&passphrase=string&secureOptions=number&client_encoding=string&application_name=string&fallback_application_name=string&max=number&query_timeout=idleTimeoutMillis=number&poolSize=number&binary=boolean&keepAlive=boolean
When using Postgres with your Parse app, you need to manage your indexes yourself.
Details about the configuration options can be found on pg-promise. Some useful combinations are below:
postgres://localhost:5432/db?ca=/path/to/file
postgres://localhost:5432/db?ssl=true&rejectUnauthorized=false
postgres://localhost:5432/db?ssl=boolean&client_encoding=string&application_name=string&fallback_application_name=string&poolSize=number&binary=boolean&keepAlive=boolean
Parse Server is meant to be mounted on an Express app. Express is a web framework for Node.js. The fastest way to get started is to clone the Parse Server repo, which at its root contains a sample Express app with the Parse API mounted.
The constructor returns an API object that conforms to an Express Middleware. This object provides the REST endpoints for a Parse app. Create an instance like so:
const api = new ParseServer({
databaseURI: 'mongodb://your.mongo.uri',
cloud: './cloud/main.js',
appId: 'myAppId',
fileKey: 'myFileKey',
masterKey: 'mySecretMasterKey',
push: { ... }, // See the Push wiki page
filesAdapter: ...,
});
The parameters are as follows:
databaseURI
: Connection string for your database.cloud
: Path to your app’s Cloud Code.appId
: A unique identifier for your app.fileKey
: A key that specifies a prefix used for file storage. For migrated apps, this is necessary to provide access to files already hosted on Parse.masterKey
: A key that overrides all permissions. Keep this secret.clientKey
: The client key for your app. (optional)restAPIKey
: The REST API key for your app. (optional)javascriptKey
: The JavaScript key for your app. (optional)dotNetKey
: The .NET key for your app. (optional)push
: An object containing push configuration. See PushfilesAdapter
: An object that implements the FilesAdapter interface. For example, the S3 files adapterauth
: Configure support for 3rd party authentication.maxUploadSize
: Maximum file upload size. Make sure your server does not restrict max request body size (e.g. nginx.conf client_max_body_size 100m;
)The Parse Server object was built to be passed directly into app.use
, which will mount the Parse API at a specified path in your Express app:
const express = require('express');
const ParseServer = require('parse-server').ParseServer;
const app = express();
const api = new ParseServer({ ... });
// Serve the Parse API at /parse URL prefix
app.use('/parse', api);
const port = 1337;
app.listen(port, function() {
console.log('parse-server-example running on port ' + port + '.');
});
And with that, you will have a Parse Server running on port 1337, serving the Parse API at /parse
.
Parse Server does not require the use of client-side keys. This includes the client key, JavaScript key, .NET key, and REST API key. The Application ID is sufficient to secure your app.
However, you have the option to specify any of these four keys upon initialization. Upon doing so, Parse Server will enforce that any clients passing a key matches. The behavior is consistent with hosted Parse.
Starting parse-server
2.6.5, it is possible to specify a readOnlyMasterKey
. When using this key instead of the masterKey, the server will perform all read
operations as if they were executing with the masterKey
but will fail to execute any write
operation.
This key is especially powerful when used with parse-dashboard
. Please refer to Parse Dashboard’s documentation for more information.
To use a Parse SDK with Parse Server, change the server URL to your Parse API URL. For example, if you have Parse Server running locally mounted at /parse:
iOS / OS X / watchOS / tvOS
Swift
let configuration = ParseClientConfiguration {
$0.applicationId = "YOUR_APP_ID"
$0.clientKey = ""
$0.server = "http://localhost:1337/parse"
}
Parse.initialize(with: configuration)
Objective-C
[Parse initializeWithConfiguration:[ParseClientConfiguration configurationWithBlock:^(id<ParseMutableClientConfiguration> configuration) {
configuration.applicationId = @"YOUR_APP_ID";
configuration.clientKey = @"";
configuration.server = @"http://localhost:1337/parse";
}]];
Android
Parse.initialize(new Parse.Configuration.Builder(myContext)
.applicationId("YOUR_APP_ID")
.server("http://localhost:1337/parse/")
...
.build()
);
JavaScript
Parse.initialize("YOUR_APP_ID");
Parse.serverURL = 'http://localhost:1337/parse'
.NET
ParseClient.initialize(new ParseClient.Configuration {
ApplicationId = "YOUR_APP_ID",
Server = "http://localhost:1337/parse/"
});
PHP
ParseClient::initialize('YOUR_APP_ID', 'YOUR_CLIENT_KEY', 'YOUR_MASTER_KEY');
ParseClient::setServerURL('http://localhost:1337', 'parse'); // server url & mount path passed separately
The fastest and easiest way to start using Parse Server is to run MongoDB and Parse Server locally. Once you have a better understanding of how the project works, read on to learn how to deploy Parse Server to major infrastructure providers. If your provider is not listed here, please take a look at the list of articles from the community as someone may have already written a guide for it.
Heroku and MongoDB Atlas provide an easy way to deploy Parse Server, especially if you’re new to managing your own backend infrastructure.
Here are the steps:
New Project
.Database Access
and create a new database user with username and password. Remember these user credentials, you will need them later to connect Parse Server to the database. As user privileges choose Read and write to any database
, you can change these privileges later on and make them more restrictive according to your needs.Clusters
and create a new cluster.Collections
and create a new database.Command Line Tools
, click on Connect Instructions
and choose Connect your application
.heroku config:set DATABASE_URI=mongodb://...
git push heroku master
You may also refer to the Heroku Dev Center article on Deploying a Parse Server to Heroku.
Before you start, you’ll need:
mLab provides a Database-as-a-Service for MongoDB. They include a free tier for small sandbox databases. Create an account on mLab and then use the Single-node, Sandbox plan to get a (free) database up and running. Within the mLab wizard, you’ll need to be sure to create a user that has access to connect to the new database. Upon completion, you should be able to construct a Mongo DB connection string like the following:
mongodb://yourusername:[email protected]:yourdatabaseport/yourdatabasename
Glitch provides an easy way to instantly create and deploy Node.js applications for free. We will use it to run the parse-server-example application.
To get the example server up and running for quick testing, you can simply click the button below:
Now that the import is complete, we’ll need to make two small changes to the 🗝️.env
file, which stores private environment variables.
It should look like the following:
# Environment Config
# store your secrets and config variables in here
# only invited collaborators will be able to see your .env values
# reference these in your code with process.env.SECRET
SECRET=
MADE_WITH=
# note: .env is a shell file so there can't be spaces around =
APP_ID=myAppId
MASTER_KEY=your_master_key_here
DATABASE_URI=your_mlab_database_uri_here
SERVER_URL=https://project-name.glitch.me/parse
PARSE_SERVER_LOGS=/tmp
First, change the DATABASE_URI
value to your mLab connection string from step 1.
Next, change the project-name
portion of the SERVER_URL
value to the name of the project that was created. So, if clicking the button creates electric-dinner.glitch.me
, your SERVER_URL
value would be https://electric-dinner.glitch.me/parse
.
You can delete the SECRET
and MADE_WITH
lines, but there’s no harm in leaving them there.
It is important, for this tutorial, to leave the APP_ID
as myAppId
as the “test” page hard-codes that and expects that value.
If you’d like to keep this project, create an account on Glitch. Projects created as an anonymous user expire after five days. You can read more about the technical restrictions on free Glitch projects here.
Once you’re finished making your changes to your 🗝️.env
file, Glitch will automatically build and deploy your application. If you use the Logs feature within Glitch (click on Tools → Logs), you should see this when your app is deployed:
parse-server-example running on port 3000.
You should then be able to use the “Show” button to launch the application in the browser and get to a page that urges you to star the parse-server GitHub repository. To access the test harness page, add a trailing /test
to your URL. This should take you to a page that will allow you to exercise a few parts of the Parse Server Javascript SDK and create a dummy collection and record in your MongoDB. If you’re able to complete steps one through three on this test page, Parse Server is up and running. Optionally, you can go back to mLab.com and take a look at the data that was stored by the test harness to get a feel for how Parse Server stores data in MongoDB.
Back4App provides an easy way to deploy and host your Parse Server Apps.
Here are the steps:
If you need to migrate your local Parse Server to Back4App you can follow these guidelines.
Here are the steps:
Ubuntu
t2.micro
(is ok for testing and small projects which is Free tier eligible)key pair
. (If you create a new one click Download Key Pair
)Launch Instance
Security
Edit Inbound Rules
Add rule
and select PostgreSQL
from the dropdown menu and Anywhere-IPv4
.Save rules
Connect
SSH Client
and follow the instructionsOnce logged into the ec2 instance we perform the following tasks:
Update the local package manager apt
sudo apt update
Install NodeJS
sudo apt install nodejs
Check the install was ok, you should see the version installed.
node -v
Install npm
sudo apt install npm
Install yarn
sudo npm install yarn –g
Install PostgreSQL
sudo apt-get -y install postgresql
Once is installed, create a password for the user postgres
sudo su postgres
psql
ALTER USER postgres password 'myStrongPassword';
Quit psql typing \q
Exit postgres user typing exit
Navigate to main folder inside postgresql/version/
cd /etc/postgresql/14/main/
We need to edit two files, pg_hba.conf
and postgresql.conf
sudo nano pg_hba.conf
Scroll down the file and Add host, all, all, 0.0.0.0/0, md5
, has to be the first line before local, all, postgres, , peer
TYPE | DATABASE | USER | ADDRESS | METHOD |
---|---|---|---|---|
host | all | all | 0.0.0.0/0 | md5 |
local | all | postgres | peer |
sudo nano postgresql.conf
Search for #listen_addresses='localhost'
, uncomment the line and replace localhost
for *
Restart the PostgreSQL server
sudo service postgresql restart
Create a directory
cd ~
mkdir parse-server
cd parse-server
Run the bash script and follow the instructions, the script have some visual issues and the keys generation doesn’t work.
sh <(curl -fsSL https://raw.githubusercontent.com/parse-community/parse-server/master/bootstrap.sh)
After that, we need to setup the configuration file, use your own appId
, masterKey
and clientKey
, use random strings or some generator tool to create secured keys.
sudo nano -w config.json
This are the basic options of the config.json file, for the full list you can type parse-server --help
or refer to the full options document for more details.
{
"appId": "exampleAppId",
"masterKey": "exampleMasterKey",
"clientKey": "exampleClientKey",
"appName": "MyApp",
"cloud": "./cloud/main",
"databaseURI": "postgres://postgres:[email protected]:5432/postgres"
}
Install Parse Server globally
sudo npm install -g parse-server
Start Parse Server using the script command in the config.json
npm start
or manually with the nohup command and specifying the configuration file, this option will keep the server running even if you close the terminal
nohup parse-server config.json &
Check if Parse Server is running typing http://<IP_OR_DOMAIN>:1337
in your browser’s address bar, you should see {"error":"unauthorized"}
Install Parse Dashboard globally
sudo npm install -g parse-dashboard
Once installed, you need to configure Parse Dashboard, go to /usr/lib/node_modules/parse-dashboard/Parse-Dashboard/
and edit the file parse-dashboard-config.json
sudo nano -w parse-dashboard-config.json
This is an example of parse-dashboard.config.json.
{
"apps": [{
"serverURL": "http://example.com:1337/parse",
"appId": "exampleAppId",
"masterKey": "exampleMasterKey",
"allowInsecureHTTP": "true",
"appName": "MyApp"
}],
"users": [{
"user": "admin",
"pass": "password"
}]
}
Start Parse Dashboard
parse-dashboard
or with the nohup command and specifying the configuration file, this option will keep the dashboard running even if you close the terminal
nohup parse-dashboard --dev --config parse-dashboard-config.json &
Check if Parse Dashboard is running typing http://<IP_OR_DOMAIN>:4040
in your browser’s address bar, you should see the login form, use the user
and pass
that you set in the parse-dashboard-config.json
file.
Parse Server provides basic push notification functionality for iOS, macOS, tvOS and Android. With this feature, you can:
ParseQuery
However, there are a few caveats:
masterKey
to send push notificationsWe support most of the sending options. Check the detailed doc here. Parse Server supports the following:
channels
to target installations by channelswhere
to target installations by ParseQuery
priority
under data
for iOS push prioritypush_type
under data
for iOS push typealert
under data
for notification messagebadge
under data
for iOS badge numbersound
under data
for iOS soundcontent-available
under data
for iOS background jobcategory
under data
for iOS categorytitle
under data
for Android notification titleuri
under data
for Android notification launched URIdata
for ios and Androidbadge
under data
for iOS and Android badge numberHere is the list of sending options we do not support yet:
push_time
for scheduled pushYou will need to obtain some credentials from FCM and APNS in order to send push notifications.
If you are setting up push notifications on iOS, tvOS or macOS for the first time, we recommend you visit the raywenderlich.com’s Push Notifications tutorial or appcoda.com’s iOS Push tutorial to help you obtain a production Apple Push Certificate. Parse Server supports the PFX (.p12
) file exported from Keychain Access. Parse Server also supports the push certificate and key in .pem
format. Token-based authentication instead of a certificate is supported as well.
To get your FCM API key, go to the Firebase console and navigate to the project. Navigate to the settings of the project, and within the “Cloud Messaging” tab, you will find it, labeled “Server key”
When initializing Parse Server, you should pass an additional push configuration. For example
var server = new ParseServer({
databaseURI: '...',
cloud: '...',
appId: '...',
masterKey: '...',
push: {
android: {
apiKey: '...'
},
ios: {
pfx: '/file/path/to/XXX.p12',
passphrase: '', // optional password to your p12/PFX
bundleId: '',
production: false
}
}
});
The configuration format is
push: {
android: {
apiKey: '' // The Server API Key of FCM
},
ios: {
pfx: '', // The filename of private key and certificate in PFX or PKCS12 format from disk
passphrase: '', // optional password to your p12
cert: '', // If not using the .p12 format, the path to the certificate PEM to load from disk
key: '', // If not using the .p12 format, the path to the private key PEM to load from disk
bundleId: '', // The bundle identifier associated with your app
production: false // Specifies which APNS environment to connect to: Production (if true) or Sandbox
}
}
For iOS, if you would like to use token-based authentication instead of certificates, you should use the following configuration format
push: {
ios: {
token: {
key: '/file/path/to/AuthKey_XXXXXXXXXX.p8',
keyId: "XXXXXXXXXX",
teamId: "YYYYYYYYYY" // The Team ID for your developer account
},
topic: 'com.domain.appname', // The bundle identifier associated with your app
production: false
}
}
If you would like to support both the dev and prod certificates, you can provide an array of configurations like
push: {
ios: [
{
pfx: '', // Dev PFX or P12
bundleId: '',
production: false // Dev
},
{
pfx: '', // Prod PFX or P12
bundleId: '',
production: true // Prod
}
],
tvos: [
// ...
],
osx: [
// ...
]
}
The configuration for macOS and tvOS works exactly as for iOS. Just add an additional configuration for each platform under the appropriate key. Please note the key for macOS is osx
and for tvOS is tvos
. If you need to support both the dev and prod certificates, you can do that for all Apple platforms like described above.
var server = new ParseServer({
databaseURI: '...',
cloud: '...',
appId: '...',
masterKey: '...',
push: {
android: {
apiKey: '...'
},
ios: {
pfx: '/file/path/to/XXX.p12',
passphrase: '', // optional password to your p12/PFX
bundleId: '',
production: false
},
osx: {
pfx: '/file/path/to/XXX.p12',
passphrase: '', // optional password to your p12/PFX
bundleId: '',
production: false
},
tvos: {
pfx: '/file/path/to/XXX.p12',
passphrase: '', // optional password to your p12/PFX
bundleId: '',
production: false
}
}
});
If you have a list of certificates, Parse Server’s strategy on choosing them is trying to match installations
’ appIdentifier
with bundleId
first. If it can find some valid certificates, it will use those certificates to establish the connection to APNS and send notifications. If it can not find, it will try to send the notifications with all certificates. Prod certificates first, then dev certificates.
Configure an app which connects to Parse Server. We have provided a detailed list of steps to configure your iOS and Android clients.
Currently Parse Server only supports sending push notifications by your masterKey
. The easiest way to do that is to curl:
curl -X POST \
-H "X-Parse-Application-Id: you_app_id" \
-H "X-Parse-Master-Key: your_master_key" \
-H "Content-Type: application/json" \
-d '{
"where": {
"deviceType": {
"$in": [
"ios",
"android"
]
}
},
"data": {
"title": "The Shining",
"alert": "All work and no play makes Jack a dull boy."
}
}'\ http://your_server_address/parse/push
Push notifications can also be sent from cloud code:
// With promises
Parse.Push.send({
where: { ... },
data: { ... }
}, { useMasterKey: true })
.then(function() {
// Push sent!
}, function(error) {
// There was a problem :(
});
// With Legacy Backbone callbacks
Parse.Push.send({
where: query,
data: {
alert: 'Test',
badge: 1,
sound: 'default'
}
}, {
useMasterKey: true,
success: function() {
// Push sent!
},
error: function(error) {
// There was a problem :(
}
});
After sending this to your Parse Server, you should see the push notifications show up on your devices.
Note: The iOS simulator cannot receive push notifications. You must run iOS apps on an iOS device.
In your Parse Server logs, you can see something similar to
// FCM request and response
{
"request": {
"params": {
"priority": "normal",
"data": {
"time": "2022-01-01T12:23:45.678Z",
"push_id": "NTDgWw7kp8",
"data": "{\"alert\":\"All work and no play makes Jack a dull boy.\"}"
}
}
},
"response": {
"multicast_id": 5318039027588186000,
"success": 1,
"failure": 0,
"canonical_ids": 0,
"results": [
{
"registration_id": "APA91bEdLpZnXT76vpkvkD7uWXEAgfrZgkiH_ybkzXqhaNcRw1KHOY0s9GUKNgneGxe2PqJ5Swk1-Vf852kpHAP0Mhoj5wd1MVXpRsRr_3KTQo_dkNd_5wcQ__yWnWLxbeM3kg_JziJK",
"message_id": "0:1455074519347821%df0f8ea7f9fd7ecd"
}
]
}
}
APNS Connected
APNS Notification transmitted to:7a7d2864598e1f65e6e02135245b7daf8ea510514e6376f072dc29d53facaa41
These logs mean that the FCM and APNS connections are working.
Parse Server provides a PushAdapter
which abstracts the way we actually send push notifications. The default implementation is ParsePushAdapter
, which uses FCM for Android push and APNS for iOS push. However, if you want to use other push providers, you can implement your own PushAdapter
. Your adapter needs to implement send(data, installations)
, which is used for sending data to the installations. You can use ParsePushAdapter
as a reference. After you implement your PushAdapter
, you can pass that instance to Parse Server like this
var server = new ParseServer({
databaseURI: '...',
cloud: '...',
appId: '...',
masterKey: '...',
push: {
adapter: your_adapter
}
});
By doing this, after Parse Server decodes the push API request and runs the installation query, your PushAdapter
’s send(data, installations)
will be called and is responsible for sending the notifications. If you provide your custom PushAdapter
, the default ParsePushAdapter
will be ignored.
The current solution provides a good starting point for push notifications. We have a lot of ideas to improve the feature:
If you’re interested in any of these features, don’t hesitate to jump in and send a PR to the repo. We would love to work with you!
If you are seeing situations where silent notifications are failing to deliver, please ensure that your payload is setting the content-available
attribute to Int(1) (or just 1 as in javascript) and not “1”. This value will be explicitly checked.
When sending a push notification to APNs you also have to set push_type
to background
for delivering silent notifications to devices running iOS 13 and later, or watchOS 6 or later.
The following will guide you through the necessary steps to configure your iOS and Android client apps to receive push notifications from Parse Server. If you haven’t yet, you will first need to prepare your APNS and FCM credentials as documented in Step 1 of the Push Notifications Quick Start.
Open up your AppDelegate.swift
, AppDelegate.m
, or AppDelegate.cs
file and make your app register for remote notifications by adding the following in your application:didFinishLaunchingWithOptions:
function:
// Swift
let types: UIUserNotificationType = [.Alert, .Badge, .Sound]
let settings = UIUserNotificationSettings(forTypes: types, categories: nil)
application.registerUserNotificationSettings(settings)
application.registerForRemoteNotifications()
// Objective-C
UIUserNotificationType userNotificationTypes = (UIUserNotificationTypeAlert |
UIUserNotificationTypeBadge |
UIUserNotificationTypeSound);
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:userNotificationTypes
categories:nil];
[application registerUserNotificationSettings:settings];
[application registerForRemoteNotifications];
// Xamarin
UIUserNotificationType notificationTypes = (UIUserNotificationType.Alert |
UIUserNotificationType.Badge |
UIUserNotificationType.Sound);
var settings = UIUserNotificationSettings.GetSettingsForTypes(notificationTypes,
new NSSet(new string[] { }));
UIApplication.SharedApplication.RegisterUserNotificationSettings(settings);
UIApplication.SharedApplication.RegisterForRemoteNotifications();
// Handle Push Notifications
ParsePush.ParsePushNotificationReceived += (object sender, ParsePushNotificationEventArgs args) => {
// Process Push Notification payload here.
};
Store the device token and handle the UI for notifications by adding the following to your main app delegate:
// Swift
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
let installation = PFInstallation.currentInstallation()
installation.setDeviceTokenFromData(deviceToken)
installation.saveInBackground()
}
func application(application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: NSError) {
if error.code == 3010 {
print("Push notifications are not supported in the iOS Simulator.")
} else {
print("application:didFailToRegisterForRemoteNotificationsWithError: %@", error)
}
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
PFPush.handlePush(userInfo)
}
// Objective-C
UIUserNotificationType userNotificationTypes = (UIUserNotificationTypeAlert |
UIUserNotificationTypeBadge |
UIUserNotificationTypeSound);
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:userNotificationTypes
categories:nil];
[application registerUserNotificationSettings:settings];
[application registerForRemoteNotifications];
// Xamarin
UIUserNotificationType notificationTypes = (UIUserNotificationType.Alert |
UIUserNotificationType.Badge |
UIUserNotificationType.Sound);
var settings = UIUserNotificationSettings.GetSettingsForTypes(notificationTypes,
new NSSet(new string[] { }));
UIApplication.SharedApplication.RegisterUserNotificationSettings(settings);
UIApplication.SharedApplication.RegisterForRemoteNotifications();
// Handle Push Notifications
ParsePush.ParsePushNotificationReceived += (object sender, ParsePushNotificationEventArgs args) => {
// Process Push Notification payload here.
};
If you configured your app correctly, installation objects will automatically be saved to Parse Server when you run your app. You can run this curl command to verify:
curl -X GET \
-H "X-Parse-Application-Id: YOUR_APP_ID" \
-H "X-Parse-Master-Key: YOUR_MASTER_KEY" \
http://your_parse_server:1337/parse/installations
Add this in your root build.gradle
file (not your module build.gradle
file):
allprojects {
repositories {
...
maven { url "https://jitpack.io" }
}
}
Then, add the library to your project build.gradle
dependencies {
implementation "com.github.parse-community.Parse-SDK-Android:fcm:latest.version.here"
}
Then, follow Google’s docs for setting up an Firebase app. Although the steps are different for setting up FCM with Parse, it is also a good idea to read over the Firebase FCM Setup. You will need to do the following:
com.google.gms.google-services
Gradle plugin (see setup guide)app/
dir.GcmBroadcastReceiver
, PushService
, com.parse.push.gcm_sender_id
if upgrading from GCM.You will need to register some services in your manifest, specifically:
<service
android:name="com.parse.fcm.ParseFirebaseInstanceIdService"
android:exported="true">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
</intent-filter>
</service>
Additional, you will register:
<service
android:name="com.parse.fcm.ParseFirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
After these services are registered in the Manifest, you then need to register the push broadcast receiver:
<receiver
android:name="com.parse.ParsePushBroadcastReceiver"
android:exported="false">
<intent-filter>
<action android:name="com.parse.push.intent.RECEIVE" />
<action android:name="com.parse.push.intent.DELETE" />
<action android:name="com.parse.push.intent.OPEN" />
</intent-filter>
</receiver>
If you need to customize the notification that is sent out from a push, you can do so by extending ParsePushBroadcastReceiver
with your own class and registering it instead in the Manifest.
Create an Installation
object by adding the following to the onCreate
method of your Application
class:
// Native: Application.java
public void onCreate() {
// ...
ParseInstallation.getCurrentInstallation().saveInBackground();
}
// Xamarin: Application.cs
// IMPORTANT: Change "parsexamarinpushsample" to match your namespace.
[Application(Name = "parsexamarinpushsample.ParseApplication")]
class ParseApplication : Application {
// ...
public override void OnCreate() {
base.OnCreate();
// ...
ParsePush.ParsePushNotificationReceived += ParsePush.DefaultParsePushNotificationReceivedHandler;
}
}
If you configured your app correctly, installation objects will automatically be saved to Parse Server when you run your app. You can run this curl command to verify:
curl -X GET \
-H "X-Parse-Application-Id: YOUR_APP_ID" \
-H "X-Parse-Master-Key: YOUR_MASTER_KEY" \
http://your_parse_server:1337/parse/installations
Note that GCM push support is deprecated and FCM should be used instead, but instructions for GCM setup can be found here
Class level permissions are a security feature from that allows one to restrict access on a broader way than the ACL based permissions.
requiresAuthentication
If you want to restrict access to a full class to only authenticated users, you can use the requiresAuthentication
class level permission. For example, you want to allow your authenticated users to find
and get
objects from your application and your admin users to have all privileges, you would set the CLP:
// PUT http://localhost:1337/schemas/:className
// Set the X-Parse-Application-Id and X-Parse-Master-Key header
// body:
{
classLevelPermissions:
{
"find": {
"requiresAuthentication": true,
"role:admin": true
},
"get": {
"requiresAuthentication": true,
"role:admin": true
},
"create": { "role:admin": true },
"update": { "role:admin": true },
"delete": { "role:admin": true },
}
}
Note that this is in no way securing your content. If you allow anyone to log in to your server, any client will be able to query this object.
Parse Server allows developers to choose from several options when hosting files:
GridStoreAdapter
, which is backed by MongoDB;S3Adapter
, which is backed by Amazon S3; orGCSAdapter
, which is backed by Google Cloud Storage; orFSAdapter
, local file storageGridStoreAdapter
is used by default and requires no setup, but if you’re interested in using S3 or Google Cloud Storage, additional configuration information is available below.
When using files on Parse, you will need to use the publicServerURL
option in your Parse Server config. This is the URL that files will be accessed from, so it should be a URL that resolves to your Parse Server. Make sure to include your mount point in this URL.
When using Postgres
, you will need to configure S3Adapter
, GCSAdapter
, or FSAdapter
for file support. The GridStoreAdapter
does not work with Postgres
.
GridStoreAdapter
If you are using Mongo
and don’t need file encryption, there are no additional steps needed to use the GridStoreAdapter
. If you’d like to enable file encryption follow these instructions to configure Parse Server to use GridStoreAdapter
with file encryption.
File encryption is available in parse-server 4.4.0+. The GridStoreAdapter
can encrypt files at rest in Mongo
using AES256-GCM, allowing the adapter to detect if files are tampered with.
To use, simply do any of the following:
PARSE_SERVER_ENCRYPTION_KEY
encryptionKey="Your file encryptionKey"
.An example starting your Parse Server in index.js
is below:
const api = new ParseServer({
databaseURI: databaseUri || 'mongodb://localhost:27017/dev',
cloud: process.env.PARSE_SERVER_CLOUD || __dirname + '/cloud/main.js',
appId: process.env.PARSE_SERVER_APPLICATION_ID || 'myAppId',
masterKey: process.env.PARSE_SERVER_MASTER_KEY || '',
encryptionKey: process.env.PARSE_SERVER_ENCRYPTION_KEY, //Add your file key here. Keep it secret
...
});
Be sure not to lose your key or change it after encrypting files.
When this is the case, it is recommended to start up a development parse-server (or a separate process from your main process) that has the same configuration as your production server. On the development server, initialize the file adapter as above with the new key and do the following after initialization in your index.js
:
//You probably want to back up your unencrypted files before doing this.
//This can take awhile depending on how many files and how large they are. It will attempt to rotate the key of all files in your filesSubDirectory
const {rotated, notRotated} = await api.filesAdapter.rotateEncryptionKey();
console.log('Files rotated to newKey: ' + rotated);
console.log('Files that couldn't be rotated to newKey: ' + notRotated);
Periodically you may want to rotate your encryptionKey for security reasons. When this is the case, it is recommended to start up a development parse-server (or a separate process from your main process) that has the same configuration as your production server. On the development server, initialize the file adapter with the new key and do the following in your index.js
(you will need your oldKey
):
//This can take awhile depending on how many files and how large they are. It will attempt to rotate the key of all files in your filesSubDirectory
const {rotated, notRotated} = await api.filesAdapter.rotateEncryptionKey({oldKey: oldKey});
console.log('Files rotated to newKey: ' + rotated);
console.log('Files that couldn't be rotated to newKey: ' + notRotated);
When this is the case, it is recommended to start up a development parse-server (or a separate process from your main process) that has the same configuration as your production server. Different from the previous examples, don’t initialize your fileAdapter with a encryptionKey
. Pass in your oldKey
to rotateEncryptionKey()
.
const api = new ParseServer({
databaseURI: databaseUri || 'mongodb://localhost:27017/dev',
cloud: process.env.PARSE_SERVER_CLOUD || __dirname + '/cloud/main.js',
appId: process.env.PARSE_SERVER_APPLICATION_ID || 'myAppId',
masterKey: process.env.PARSE_SERVER_MASTER_KEY || '',
//No encryptionKey here
...
});
//This can take awhile depending on how many files and how larger they are. It will attempt to rotate the key of all files in your filesSubDirectory
//It is not recommended to do this on the production server, deploy a development server to complete the process.
const {rotated, notRotated} = await api.filesAdapter.rotateEncryptionKey({oldKey: oldKey});
console.log('Files rotated to unencrypted with noKey: ' + rotated);
console.log('Files that couldn't be rotated to unencrypted with noKey: ' + notRotated);
This is useful if for some reason there were errors and some of the files weren’t rotated and returned in notRotated
. The process is the same as the previous examples, but pass in your oldKey
along with the array of fileNames
to rotateEncryptionKey()
.
//This can take awhile depending on how many files and how large they are. It will attempt to rotate the key of all files in your filesSubDirectory
const {rotated, notRotated} = await api.filesAdapter.rotateEncryptionKey({oldKey: oldKey, fileNames: ["fileName1.png","fileName2.png"]});
console.log('Files rotated to newKey: ' + rotated);
console.log('Files that couldn't be rotated to newKey: ' + notRotated);
S3Adapter
If you’d like to use Amazon S3, follow these instructions to configure Parse Server to use S3Adapter
.
First you will create a bucket in S3 to hold these files.
directAccess
to work. All other defaults are OK.{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:*"
],
"Resource": [
"arn:aws:s3:::BUCKET_NAME",
"arn:aws:s3:::BUCKET_NAME/*"
]
}
]
}
Writing to your Amazon S3 bucket from Parse Server is as simple as configuring and using the S3 files adapter.
If you’re running a standalone Parse Server, you can use the following environment variables to configure the S3 adapter:
Variable Name | Description | Notes |
---|---|---|
PARSE_SERVER_FILES_ADAPTER | Set this variable to ‘./Files/S3Adapter.js’. | Required |
S3_ACCESS_KEY | The AWS access key for a user that has the required permissions. | Required |
S3_SECRET_KEY | The AWS secret key for the user. | Required |
S3_BUCKET | The name of your S3 bucket. Needs to be globally unique in all of S3. | Required |
S3_REGION | The AWS region to connect to. | Optional. Default: ‘us-east-1’ |
S3_BUCKET_PREFIX | Create all the files with the specified prefix added to the filename. Can be used to put all the files for an app in a folder with ‘folder/’. | Optional. |
S3_DIRECT_ACCESS | Whether reads are going directly to S3 or proxied through your Parse Server. If set to true, files will be made publicly accessible, and reads will not be proxied. | Optional. Default: false |
If you’re using Node.js/Express:
...
var S3Adapter = require('parse-server').S3Adapter;
var api = new ParseServer({
databaseURI: databaseUri || 'mongodb://localhost:27017/dev',
appId: process.env.APP_ID || 'APPLICATION_ID',
masterKey: process.env.MASTER_KEY || 'MASTER_KEY',
...
filesAdapter: new S3Adapter(
"S3_ACCESS_KEY",
"S3_SECRET_KEY",
"S3_BUCKET",
{directAccess: true}
),
...
});
Don’t forget to change S3_ACCESS_KEY, S3_SECRET_KEY and S3_BUCKET to their correct value.
new S3Adapter(accessKey, secretKey, bucket, options)
Parameter | Description | Notes |
---|---|---|
accessKey | The AWS access key for a user that has the required permissions | Required. |
secretKey | The AWS secret key for the user | Required. |
bucket | The name of your S3 bucket. | Required. |
options | JavaScript object (map) that can contain: | |
region | Key in options . Set the AWS region to connect to. |
Optional. Default: us-east-1 |
bucketPrefix | Key in options . Set to create all the files with the specified prefix added to the filename. Can be used to put all the files for an app in a folder with ‘folder/’. |
Optional. Default: null |
directAccess | Key in options . Controls whether reads are going directly to S3 or proxied through your Parse Server. If set to true, files will be made publicly accessible, and reads will not be proxied. |
Optional. Default: false |
baseUrl | Key in options . The base URL the file adapter uses to determine the file location for direct access. |
Optional. Default: null . To be used when directAccess=true . When set the file adapter returns a file URL in format baseUrl/bucketPrefix + filename . Example for baseUrl='http://domain.com/folder' and bucketPrefix='prefix_' the returned file location is http://domain.com/folder/prefix_file.txt . |
baseUrlDirect | Key in options . Is true if the file adapter should ignore the bucket prefix when determining the file location for direct access. |
Optional. Default: false . To be used when directAccess=true and baseUrl is set. When set to true , the file adapter returns a file URL in format baseUrl/filename . Example for baseUrl='http://domain.com/folder' and baseUrlDirect=true the returned file location is http://domain.com/folder/file.txt . |
globalCacheControl | Key in options . The Cache-Control http header to set in the file request. |
Optional. Default: null . Example: public, max-age=86400 for 24 hrs caching. More info here. |
Digital Ocean Spaces is an S3-compatible storage service in the cloud. See their documentation for more details.
const s3Options = {
bucket: "SPACES_BUCKET_NAME",
baseUrl: "SPACES_BASE_URL",
region: "SPACES_REGION",
bucketPrefix: "SPACES_BUCKET_PREFIX",
s3overrides: {
accessKeyId: "SPACES_ACCESS_KEY",
secretAccessKey: "SPACES_SECRET_KEY",
endpoint: 'SPACES_ENDPOINT'
}
};
Linode Object Storage is an S3-compatible storage service in the cloud. See their documentation for more details.
const s3Options = {
bucket: "S3_BUCKET_NAME",
baseUrl: "S3_BASE_URL", // https://myBucket.myRegion.linodeobjects.com
region: "S3_REGION", // possible values: eu-central-1 or us-east-1
s3overrides: {
accessKeyId: "S3_ACCESS_KEY", // bucket access key
secretAccessKey: "S3_SECRET_KEY", // bucket secret key
endpoint: "S3_ENDPOINT", // regionName.linodeobjects.com
},
};
Backblaze B2 Cloud Storage is an S3-compatible storage service in the cloud. See their documentation for more details.
const s3Options = {
bucket: "S3_BUCKET",
baseUrl: "S3_BASE_URL", // taken from BackBlaze, normally https://BUCKET.s3.REGION.backblazeb2.com
signatureVersion: 'v4',
region: 'us-west-000',
s3overrides: {
endpoint: "S3_ENDPOINT", // check backblaze bucket endpoint
accessKeyId: "S3_ACCESS_KEY",
secretAccessKey: "S3_SECRET_KEY"
},
};
GCSAdapter
Unlike the S3 adapter, you must create a new Cloud Storage bucket, as this is not created automatically. See the Google Cloud guide on Authentication for more details.
To generate a private key in the Cloud Platform Console follow these instructions.
Starting 2.2.6, GCS Adapter is not provided by default by parse-server. To install run:
npm install --save parse-server-gcs-adapter
Writing to your Google Cloud Storage bucket from Parse Server is as simple as configuring and using the GCS files adapter.
You can use Google Cloud Storage to host your static files by setting the following environment variables:
Variable Name | Description | Notes |
---|---|---|
PARSE_SERVER_FILES_ADAPTER | Set this variable to ‘parse-server-gcs-adapter’. | Required. |
GCP_PROJECT_ID | The project ID from the Google Developer’s Console. | Required. |
GCP_KEYFILE_PATH | Full path to the a .json, .pem, or .p12 key downloaded from the Google Developers Console. | Required. |
GCS_BUCKET | The name of your GCS bucket. | Required. |
GCS_BUCKET_PREFIX | Create all the files with the specified prefix added to the filename. Can be used to put all the files for an app in a folder with ‘folder/’. | Optional. |
GCS_DIRECT_ACCESS | Whether reads are going directly to GCS or proxied through your Parse Server. | Optional. Default: false |
If you’re using Node.js/Express:
...
var GCSAdapter = require('parse-server-gcs-adapter');
var api = new ParseServer({
databaseURI: databaseUri || 'mongodb://localhost:27017/dev',
appId: process.env.APP_ID || 'APPLICATION_ID',
masterKey: process.env.MASTER_KEY || 'MASTER_KEY',
...
filesAdapter: new GCSAdapter(
"GCP_PROJECT_ID",
"GCP_KEYFILE_PATH",
"GCS_BUCKET",
{directAccess: true}
),
...
});
new GCSAdapter(projectId, keyfilePath, bucket, options)
Parameter | Description | Notes |
---|---|---|
projectId | The project ID from the Google Developer’s Console. | Required. |
keyfilePath | Full path to the a .json, .pem, or .p12 key downloaded from the Google Developers Console. | Required. |
bucket | The name of your GCS bucket. | Required. |
options | JavaScript object (map) that can contain: | |
bucketPrefix | Key in options . Set to create all the files with the specified prefix added to the filename. Can be used to put all the files for an app in a folder with ‘folder/’. |
Optional. Default: ‘’ |
directAccess | Key in options . Controls whether reads are going directly to GCS or proxied through your Parse Server. |
Optional. Default: false |
FSAdapter
To use the FSAdapter, simply initialize your Parse Server in index.js
by doing the following:
var FSFilesAdapter = require('@parse/fs-files-adapter');
var fsAdapter = new FSFilesAdapter({
"filesSubDirectory": "my/files/folder" // optional, defaults to ./files
});
var api = new ParseServer({
appId: 'my_app',
masterKey: 'master_key',
filesAdapter: fsAdapter
})
FSAdapter
with multiple instances of Parse ServerWhen using parse-server-fs-adapter across multiple Parse Server instances it’s important to establish “centralization” of your file storage (this is the same premise as the other file adapters, you are sending/recieving files through a dedicated link). You can accomplish this at the file storage level by Samba mounting (or any other type of mounting) your storage to each of your parse-server instances, e.g if you are using Parse Server via docker (volume mount your SMB drive to - /Volumes/SMB-Drive/MyParseApp1/files:/parse-server/files
). All Parse Server instances need to be able to read/write to the same storage in order for parse-server-fs-adapter to work properly with parse-server. If the file storage isn’t centralized, parse-server will have trouble locating files and you will get random behavior on client-side.
File encryption is available in parse-server-fs-adapter 1.1.0+. The FSAdapter
can encrypt files at rest for local storage using AES256-GCM, allowing the adapter to detect if files are tampered with.
To use, simply do the same as above, but add a encryptionKey
:
var FSFilesAdapter = require('@parse/fs-files-adapter');
var fsAdapter = new FSFilesAdapter({
"filesSubDirectory": "my/files/folder", // optional, defaults to ./files
"encryptionKey": "someKey" //mandatory if you want to encrypt files
});
var api = new ParseServer({
appId: 'my_app',
masterKey: 'master_key',
filesAdapter: fsAdapter
})
Be sure not to lose your key or change it after encrypting files.
When this is the case, it is recommended to start up a development parse-server (or a separate process from your main process) that has the same configuration as your production server. On the development server, initialize the file adapter as above with the new key and do the following after initialization in your index.js
:
//You probably want to back up your unencrypted files before doing this.
//This can take awhile depending on how many files and how large they are. It will attempt to rotate the key of all files in your filesSubDirectory
const {rotated, notRotated} = await api.filesAdapter.rotateEncryptionKey();
console.log('Files rotated to newKey: ' + rotated);
console.log('Files that couldn't be rotated to newKey: ' + notRotated);
Periodically you may want to rotate your encryptionKey
for security reasons. When this is the case, it is recommended to start up a development parse-server (or a separate process from your main process) that has the same configuration as your production server. On the development server, initialize the file adapter with the new key and do the following in your index.js
(you will need your oldKey
):
//This can take awhile depending on how many files and how large they are. It will attempt to rotate the key of all files in your filesSubDirectory
const {rotated, notRotated} = await api.filesAdapter.rotateEncryptionKey({oldKey: oldKey});
console.log('Files rotated to newKey: ' + rotated);
console.log('Files that couldn't be rotated to newKey: ' + notRotated);
When this is the case, it is recommended to start up a development parse-server (or a separate process from your main process) that has the same configuration as your production server. Different from the previous examples, don’t initialize your fileAdapter with a encryptionKey
. Pass in your oldKey
to rotateEncryptionKey()
.
const api = new ParseServer({
databaseURI: databaseUri || 'mongodb://localhost:27017/dev',
cloud: process.env.PARSE_SERVER_CLOUD || __dirname + '/cloud/main.js',
appId: process.env.PARSE_SERVER_APPLICATION_ID || 'myAppId',
masterKey: process.env.PARSE_SERVER_MASTER_KEY || '',
filesAdapter: new FSFilesAdapter(), //No encryptionKey supplied
...
});
//This can take awhile depending on how many files and how larger they are. It will attempt to rotate the key of all files in your filesSubDirectory
//It is not recommended to do this on the production server, deploy a development server to complete the process.
const {rotated, notRotated} = await api.filesAdapter.rotateEncryptionKey({oldKey: oldKey});
console.log('Files rotated to unencrypted with noKey: ' + rotated);
console.log('Files that couldn't be rotated to unencrypted with noKey: ' + notRotated);
This is useful if for some reason there were errors and some of the files weren’t rotated and returned in notRotated
. The process is the same as the previous examples, but pass in your oldKey
along with the array of fileNames
to rotateEncryptionKey()
.
//This can take awhile depending on how many files and how large they are. It will attempt to rotate the key of all files in your filesSubDirectory
const {rotated, notRotated} = await api.filesAdapter.rotateEncryptionKey({oldKey: oldKey, fileNames: ["fileName1.png","fileName2.png"]});
console.log('Files rotated to newKey: ' + rotated);
console.log('Files that couldn't be rotated to newKey: ' + notRotated);
By default, parse-server provides an internal cache layer to speed up schema verifications, user, roles and sessions lookup.
In some cases, in distributed environment, you may want to use a distributed cache like Redis.
parse-server comes with an optional redis cache adapter.
Those cache adapters can be cleaned at anytime internally, you should not use them to cache data and you should let parse-server manage their data lifecycle.
var RedisCacheAdapter = require('parse-server').RedisCacheAdapter;
var redisOptions = {url: 'YOUR REDIS URL HERE'}
var redisCache = new RedisCacheAdapter(redisOptions);
var api = new ParseServer({
databaseURI: databaseUri || 'mongodb://localhost:27017/dev',
appId: process.env.APP_ID || 'APPLICATION_ID',
masterKey: process.env.MASTER_KEY || 'MASTER_KEY',
...
cacheAdapter: redisCache,
...
});
The redisOptions
are passed directly to the redis.createClient method. For more information refer to the redis.createClient documentation.
Note that at the moment, only passing a single argument is supported.
The cache adapter can flush the redis database at anytime. It is best to not use the same redis database for other services. A different redis database can be chosen by providing a different database number to redisOptions
. By default redis has 16 databases (indexed from 0 to 15).
Parse.Query
is one of the key concepts for Parse. It allows you to retrieve Parse.Object
s by specifying some conditions, making it easy to build apps such as a dashboard, a todo list or even some strategy games. However, Parse.Query
is based on a pull model, which is not suitable for apps that need real-time support.
Suppose you are building an app that allows multiple users to edit the same file at the same time. Parse.Query
would not be an ideal tool since you can not know when to query from the server to get the updates.
To solve this problem, we introduce Parse LiveQuery. This tool allows you to subscribe to a Parse.Query
you are interested in. Once subscribed, the server will notify clients whenever a Parse.Object
that matches the Parse.Query
is created or updated, in real-time.
Parse LiveQuery contains two parts, the LiveQuery server and the LiveQuery clients. In order to use live queries, you need to set up both of them.
The LiveQuery server should work with a Parse Server. The easiest way to setup the LiveQuery server is to make it run with the Parse Server in the same process. When you initialize the Parse Server, you need to define which Parse.Object
classes you want to enable LiveQuery like this:
let api = new ParseServer({
...,
liveQuery: {
classNames: ['Test', 'TestAgain']
}
});
After that, you need to initialize a LiveQuery server like this:
// Initialize a LiveQuery server instance, app is the express app of your Parse Server
let httpServer = require('http').createServer(app);
httpServer.listen(port);
var parseLiveQueryServer = ParseServer.createLiveQueryServer(httpServer);
The ws
protocol URL of the LiveQuery server is the hostname and port which the httpServer
is listening to. For example, if the httpServer
is listening to localhost:8080
, the ws
protocol of the LiveQuery server is ws://localhost:8080/
. We will allow you to customize the path of ws
protocol URL of the LiveQuery server later, currently it is fixed and you can not set path.
We provide JavaScript, Android and iOS LiveQuery Clients for now. Lets use the JavaScript client as an example. In order to use LiveQuery, you need to initialize a Parse.Query
object and subscribe to it.
let query = new Parse.Query('People');
query.equalTo('name', 'Mengyan');
let subscription = await query.subscribe();
After you get the subscription
, you can use it to receive the updates of the related Parse.Object
. For example, if someone creates a People
object whose name
field is Mengyan
, then you can get the People
object like this:
subscription.on('create', (people) => {
console.log(people.get('name')); // This should output Mengyan
});
After that, if someone updates this People
object like changing its score to 100, then you can get the People
object like this:
subscription.on('update', (people) => {
console.log(people.get('score')); // This should output 100
});
If you are done with the LiveQuery, you can simply unsubscribe the subscription
to finish receiving events
subscription.unsubscribe();
We support five types of event:
create
enter
update
leave
delete
You can check the LiveQuery protocol specification to learn more about each event type.
For more details about the JavaScript LiveQuery Client SDK, check out the open source code and the Live Query section in the JavaScript Guide.
For the iOS LiveQuery Client SDK, check out the open source code.
The LiveQuery Protocol is the key to the Parse LiveQuery. The clients and server communicate through WebSocket using this protocol. Clients can follow the protocol to connect to the LiveQuery server, subscribe/unsubscribe a Parse.Query
and get updates from the LiveQuery server.
The LiveQuery protocol is a simple protocol that encapsulates messages in JSON strings and runs over a WebSocket connection. You can find the specification in the For the specification, check out the Parse Server wiki page.
The full configuration of the LiveQuery server should look like this:
{
appId: 'myAppId',
masterKey: 'myMasterKey',
keyPairs: {
"restAPIKey": "",
"javascriptKey": "",
"clientKey": "",
"windowsKey": "",
"masterKey": ""
},
serverURL: 'serverURL',
websocketTimeout: 10 * 1000,
cacheTimeout: 60 * 600 * 1000,
logLevel: 'VERBOSE'
}
Options
appId
- Required. This string should match the appId
in use by your Parse Server. If you deploy the LiveQuery server alongside Parse Server, the LiveQuery server will try to use the same appId
.masterKey
- Required. This string should match the masterKey
in use by your Parse Server. If you deploy the LiveQuery server alongside Parse Server, the LiveQuery server will try to use the same masterKey
.serverURL
- Required. This string should match the serverURL
in use by your Parse Server. If you deploy the LiveQuery server alongside Parse Server, the LiveQuery server will try to use the same serverURL
.keyPairs
- Optional. A JSON object that serves as a whitelist of keys. It is used for validating clients when they try to connect to the LiveQuery server. Check the following Security section and our protocol specification for details.websocketTimeout
- Optional. Number of milliseconds between ping/pong frames. The WebSocket server sends ping/pong frames to the clients to keep the WebSocket alive. This value defines the interval of the ping/pong frame from the server to clients. Defaults to 10 * 1000 ms (10 s).cacheTimeout
- Optional. Number in milliseconds. When clients provide the sessionToken
to the LiveQuery server, the LiveQuery server will try to fetch its ParseUser
’s objectId from parse server and store it in the cache. The value defines the duration of the cache. Check the following Security section and our protocol specification for details. Defaults to 30 * 24 * 60 * 60 * 1000 ms (~30 days).logLevel
- Optional. This string defines the log level of the LiveQuery server. We support VERBOSE
, INFO
, ERROR
, NONE
. Defaults to INFO
.The LiveQuery server is a separate server from Parse Server. As shown in the picture, it mainly contains four components at the runtime.
Parse.Object
. When a Parse.Object
changes, it will publish a message to the subscribers. The message contains the original Parse.Object
and the new Parse.Object
. The Publisher is inside the Parse Server at the runtime.Parse.Object
fulfills a Parse.Query
, it will get the event message from LiveQuery component and send it to the clients.Parse.Object
updates from the Subscriber, it can do the query matching and generate the event messages for clients.Based on your usage, different components of the LiveQuery server may become the bottleneck. If you app has high throughput, the Publisher/Subscriber may have problems. If you subscribe to many complex Parse.Query
s, the LiveQuery component may cause issues. If you need to maintain lots of clients, the WebSocketServer may be the bottleneck. Thus, we highly recommend you to do the load testing for your app if you want to use LiveQuery server in production.
In general, our suggestion to make the LiveQuery server scalable is to separate the Parse Server with the LiveQuery server and add more LiveQuery server instances based on your need. To help you do this, we use Redis to implement a Publisher and Subscriber. If you want to use that, the only thing you need to do is to provide the Redis server address when you initialize the Parse Server and LiveQuery server like this:
let api = new ParseServer({
...,
liveQuery: {
classNames: ['Test', 'TestAgain'],
redisURL: 'redis://localhost:6379'
}
});
...
let httpServer = require('http').createServer(app);
httpServer.listen(port);
var parseLiveQueryServer = ParseServer.createLiveQueryServer(httpServer, {
...,
redisURL: 'redis://localhost:6379'
});
This redis database should be different from the redis database used for RedisCacheAdapter
.
The architecture of the whole LiveQuery system after you use Redis should be like this:
For example, if you use Heroku to deploy your Live Query server, after you setup the Redis with the LiveQuery server, you can simply add more dynos to make your app more scalable like this:
The LiveQuery server provides two ways to secure your app. The first one is key matching. If you provide key pairs when you initialize the LiveQuery server, when clients try to connect to LiveQuery server, they have to provide the necessary key pairs. Otherwise, the connection will be refused.
The second one is ACL. For what is ACL, you can check the definition here. When clients try to connect and subscribe to the LiveQuery server, they can provide their sessionToken
. If you give your Parse.Object
proper ACL, when the LiveQuery server get the updates of the Parse.Object
, it will try to match Parse.Object
’s ACL with the sessionToken
of clients or their subscriptions. The event will be only sent to clients whose sessionToken
matches the Parse.Object
’s ACL.
The JavaScript LiveQuery client is provided as part of the Parse JavaScript SDK as of version 1.8.0. A separate LiveQuery client library is available for iOS / OS X and Android.
Please refer to the NGINX documentation in order to allow a proper handling of the LiveQuery server that relies on web sockets
Parse Server supports 3rd party authentication with
Configuration options for these 3rd-party modules is done with the auth
option passed to Parse Server:
{
auth: {
twitter: {
consumer_key: "", // REQUIRED
consumer_secret: "" // REQUIRED
},
facebook: {
appIds: "FACEBOOK APP ID"
}
}
}
Below, you will find all expected payloads for logging in with a 3rd party auth.
Note, most of them don’t require a server configuration so you can use them directly, without particular server configuration.
authData
{
"facebook": {
"id": "user's Facebook id number as a string",
"access_token": "an authorized Facebook access token for the user",
"expiration_date": "token expiration date of the format: yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
}
}
Learn more about Facebook login.
authData
{
"twitter": {
"id": "user's Twitter id number as a string",
"consumer_key": "your application's consumer key",
"consumer_secret": "your application's consumer secret",
"auth_token": "an authorized Twitter token for the user with your application",
"auth_token_secret": "the secret associated with the auth_token"
}
}
The options passed to Parse server:
{
auth: {
twitter: {
consumer_key: "", // REQUIRED
consumer_secret: "" // REQUIRED
},
}
}
Learn more about Twitter login.
authData
{
"anonymous": {
"id": "random UUID with lowercase hexadecimal digits"
}
}
authData
As of Parse Server 3.5.0 you can use Sign In With Apple.
{
"apple": {
"id": "user",
"token": "the identity token for the user"
}
}
Using Apple Sign In on a iOS device will give you a ASAuthorizationAppleIDCredential.user
string for the user identifier, which can be match the sub
component of the JWT identity token.
Using Apple Sign In through the Apple JS SDK or through the REST service will only give you the JWT identity token (id_token
) which you’ll have to decompose to obtain the user identifier in its sub
component. As an example you could use something like JSON.parse(atob(token.split(".")[1])).sub
.
{
auth: {
apple: {
clientId: 'com.example.app', // optional, for extra validation; replace with the bundle ID provided by Apple.
},
}
}
Learn more about Sign In With Apple.
authData
{
"github": {
"id": "user's Github id (string)",
"access_token": "an authorized Github access token for the user"
}
}
authData
Google oauth supports validation of id_token’s and access_token’s.
{
"google": {
"id": "user's Google id (string)",
"id_token": "an authorized Google id_token for the user (use when not using access_token)",
"access_token": "an authorized Google access_token for the user (use when not using id_token)"
}
}
authData
{
"instagram": {
"id": "user's Instagram id (string)",
"access_token": "an authorized Instagram access token for the user",
"apiURL": "an api url to make requests. Default: https://api.instagram.com/v1/"
}
}
authData
{
"keycloak": {
"access_token": "access token from keycloak JS client authentication",
"id": "the id retrieved from client authentication in Keycloak",
"roles": ["the roles retrieved from client authentication in Keycloak"],
"groups": ["the groups retrieved from client authentication in Keycloak"]
}
}
The authentication module will test if the authData is the same as the userinfo oauth call, by comparing the attributes.
Copy the JSON config file generated on Keycloak (tutorial)
and paste it inside of a folder (Ex.: auth/keycloak.json
) in your server.
The options passed to Parse Server:
{
auth: {
keycloak: {
config: require(`./auth/keycloak.json`) // Required
}
}
}
The LDAP module can check if a
user can authenticate (bind) with the given credentials. Optionally, it can also check if the user is in a certain group.
This check is done using a user specified query, called an LDAP Filter.
The query should return all groups which the user is a member of. The cn
attribute of the query results is compared to groupCn
.
To build a query which works with your LDAP server, you can use a LDAP client like Apache Directory Studio.
{
"ldap": {
"url": "ldap://host:port",
"suffix": "the root of your LDAP tree",
"dn": "Bind dn. is replaced with the id suppied in authData",
"groupCn": "Optional. A group which the user must be a member of.",
"groupFilter": "Optional. An LDAP filter for finding groups which the user is part of. is replaced with the id supplied in authData."
}
}
If either groupCN
or groupFilter
is not specified, the group check is not performed.
Example Configuration (this works with the public LDAP test server hosted by Forumsys):
{
"ldap": {
"url": "ldap://ldap.forumsys.com:389",
"suffix": "dc=example,dc=com",
"dn": "uid=, dc=example, dc=com",
"groupCn": "Chemists",
"groupFilter": "(&(uniqueMember=uid=,dc=example,dc=com)(objectClass=groupOfUniqueNames))"
}
}
authData:
{
"authData": {
"ldap": {
"id": "user id",
"password": "password"
}
}
}
authData
{
"linkedin": {
"id": "user's LinkedIn id (string)",
"access_token": "an authorized LinkedIn access token for the user",
"is_mobile_sdk": true|false // set to true if you acquired the token through LinkedIn mobile SDK
}
}
authData
{
"meetup": {
"id": "user's Meetup id (string)",
"access_token": "an authorized Meetup access token for the user"
}
}
authData
{
"microsoft": {
"id": "user's microsoft id (string)", // required
"access_token": "an authorized microsoft graph access token for the user", // required
"mail": "user's microsoft email (string)"
}
}
Learn more about Microsoft Graph Auth Overview.
To get access on behalf of a user.
authData
As of Parse Server 3.7.0 you can use PhantAuth.
{
"phantauth": {
"id": "user's PhantAuth sub (string)",
"access_token": "an authorized PhantAuth access token for the user",
}
}
Learn more about PhantAuth.
authData
{
"qq": {
"id": "user's QQ id (string)",
"access_token": "an authorized QQ access token for the user"
}
}
authData
{
"spotify": {
"id": "user's spotify id (string)",
"access_token": "an authorized spotify access token for the user"
}
}
authData
{
"vkontakte": {
"id": "user's vkontakte id (string)",
"access_token": "an authorized vkontakte access token for the user"
}
}
{
auth: {
vkontakte: {
appSecret: "", // REQUIRED, your vkontakte application secret
appIds: "" // REQUIRED, your vkontakte application id
},
}
}
authData
{
"wechat": {
"id": "user's wechat id (string)",
"access_token": "an authorized wechat access token for the user"
}
}
authData
{
"weibo": {
"id": "user's weibo id (string)",
"access_token": "an authorized weibo access token for the user"
}
}
It is possible to leverage the OAuth support with any 3rd party authentication that you bring in.
{
auth: {
my_custom_auth: {
module: "PATH_TO_MODULE" // OR object,
option1: "",
option2: "",
}
}
}
On this module, you need to implement and export those two functions validateAuthData(authData, options) {}
and validateAppId(appIds, authData, options) {}
.
For more information about custom auth please see the examples:
As of ParseServer 2.5, it is possible to set a read preference for Mongo DB queries. For a discussion of Read Preference, limitations and use cases, see the Mongo documentation for Read Preference..
Currently, read preference can only be set in cloud code. For an example see: cloud code examples
Normally, when you run a standalone Parse Server, the latest release that has been pushed to npm will be used. This is great if you are interested in just running Parse Server, but if you are developing a new feature or fixing a bug you will want to use the latest code on your development environment.
First, you will need to clone this repo if you haven’t done so yet.
git clone https://github.com/parse-community/parse-server.git
You can then link the parse-server module to the cloned repo and run npm install
:
npm link parse-server path/to/cloned/repo
npm install
You can now start Parse Server using npm start
:
npm start -- --appId APPLICATION_ID --masterKey MASTER_KEY --serverURL http://localhost:1337/parse
The following is a breakdown of the various files you will find in the Parse Server source code. Click on a filename to learn more about the purpose behind each file.
We really want Parse to be yours, to see it grow and thrive in the open source community. Please see the Contributing to Parse Server notes.
This page is a work in progress and incomplete. If you have any suggestions, please open a pull request.
Protect all Parse Server endpoints using a Firewall to mitigate the risk of malicious attempts to scape user data, flood the database and DDoS attacks.
The following is a list of design considerations to optimize data traffic and performance.
select
and exclude
to transfer only the fields that you need instead of the whole object.