Developer Docs

Introduction

ParaIO.com is the hosted service of Para - the open source backend framework. Para is a simple and flexible backend service that allows you to focus on your front-end. It is backed by the AWS cloud and runs on top of Elasticsearch and DynamoDB. Para can persist any object easily, while caching it automatically on every write request. Additionally, it supports full text search and allows you to build complex queries for finding your stored objects.

Quick start

  1. Sign in
  2. Create a new App
  3. Take note of the security credentials - access and secret keys
  4. Use one of the client libraries below or use the API directly

The quickest way to interact with Para is through the command-line tool (CLI):

$ npm install -g para-cli
$ para-cli ping
$ echo "{\"type\":\"todo\", \"name\": \"buy milk\"}" > todo.json
$ para-cli create todo.json --id todo1 --encodeId false
$ para-cli read --id todo1
$ para-cli search "type:todo"

In your own project you can create a new ParaClient instance like so:

ParaClient pc = new ParaClient("ACCESS_KEY", "SECRET_KEY");
// send a test request - this should return a JSON object of type 'app'
pc.me();

API clients

 

Quickstart examples

So you want to build something quickly? So by now you should have created an App through the web UI and got your access keys. You can use these to initialize the API client like so:

// example for server-side JavaScript
var ParaClient = require('para-client-js');
var pc = new ParaClient('ACCESS_KEY', 'SECRET_KEY');

Note: Never include your secret key in client-side code. To create a frontend client in JavaScript you can either use JWT tokens and leave the secret key blank or use the paraClient.signin() method to authenticate your users first. Once your client is initialized you can start doing real work like persisting objects and querying them.

Here’s a short list of starter projects, and we hope to expand it in the near future:

Apps

Apps allow you to have separate namespaces and data models for different purposes. Each app lives in its own separate database table and is independent from apps.

Each app has a unique identifier, like “my-custom-app”. When an object is created, Para will attach the app identifier to it automatically. Apps also have a set of data types, as set of permissions and validation constraints. Data types can be created on-the-fly, for example you can create a type called “article” and it will have be available as a new API resource at /v1/article (and in plural form /v1/articles).

Creating apps

You can create apps through the web interface. Each app is independent and cannot interact with the data of other apps.

Currently Para organizes objects in one table per app and uses a single shared search index unless that app sets shared = false. If this is the case then a separate search index is created for that app. It is possible to make Para use a single database table for all apps by prefixing id fields (e.g. app1_id1: {data}) but this is not yet implemented.

You can set custom settings to each app through the settings API /v1/_settings using GET, PUT and DELETE. These can be OAuth credentials for social apps or other configuration details that are specific for the app.

Social sign-in for apps

In v1.19 apps can have their own separate credentials for social sign-in, like an OAuth app_id and secret. These are stored within the app object and can be used to create/login users to the app that has these credentials. For example, we can send a request to /facebook_auth?appid=myapp where myapp is not the root app. This will tell Para to look for Facebook keys inside that app. This allows for each Para app to have a corresponding Facebook app (or any other app on the supported social networks, see JWT sign in).

Additionally, we’ve added two custom settings which can tell Para where to redirect users after a successful login or a login failure. These are signin_success and signin_failure (see Custom Settings). Here’s an example of all settings combined:

{
    "fb_app_id": "123U3VTNifLPqnZ1W2",
    "fb_secret": "YXBwOnBhcmE11234151667",
    "signin_success": "/dashboard",
    "signin_failure": "/signin?error"
}

Note that these settings will work for the traditional authentication flow through the browser and the standard endpoints /facebook_auth?appid=myapp, /github_auth?appid=myapp, /google_auth?appid=myapp, and the rest. The other way of authenticating users is through the JWT sign in API which requires an OAuth access token and doesn’t require stored OAuth credentials - these are not even checked because we are supplied with a ready-to-use token.

Core objects

All objects in Para extend ParaObject which gives them basic common properties like id, timestamp, name, etc. Let’s say you have a plain old Java object like this:

This allows you do call create(), update(), delete() on that object and enables search indexing automatically.

User u = new User();
u.setName("Gordon Freeman");
u.setAge(40);
// generates a new id and persists the object
String id = u.create();

And here’s what a Para object looks like as JSON when returned from the REST API:

{
  "id" : "572040968316915712",
  "timestamp" : 1446469779546,
  "type" : "user",
  "appid" : "para",
  "updated" : 1446469780024,
  "name" : "Gordon Freeman",
  "votes" : 0,
  "identifier" : "fb:1000123456789",
  "groups" : "admins",
  "active" : true,
  "email" : "g.freeman@blackmesa.com",
  "objectURI" : "/users/572040968316915712",
  "plural" : "users"
}

Fine-tuning backend operations

From version 1.18 Para objects have three new flags - stored, indexed and cached. These flags turn on and off the three main operations - persistence, indexing and caching. Developers can choose to switch off caching on a number of objects that change very often, for example. Also some objects my be hidden from search by setting the indexed: false flag. And finally you can turn off persistence completely with stored: false and thus have objects that live only in memory (and search index) but are never stored in the database.

Core types

The list below describes all of the core types that are built into Para. You can use them for your objects but you’re always free to create your own. To do that, simply POST a new object with type: mytype through the API and your new type will be automatically registered.

Note: Type definitions cannot contain the symbols / and #.

typedescription
UserDefines a basic user with a name, email, password. Used for user registration and security.
AddressDefines an address with optional geographical coordinates. Used for location based searching.
AppDefines an application within Para. Usually there’s only one existing (root) app.
TagSimple tag class which contains a tag and its frequency count. Basically any Para object can be tagged. Used for searching.
VoteDefines a user vote - negative or positive. Useful for modeling objects which can be voted on (likes, +1s, favs, etc).
TranslationHolds a translated string. Can be used for collecting translations from users.
SyspropSystem class used as a general-purpose data container. It’s basically a map.
LinkerSystem class used for implementing a many-to-many relationship between objects.
ThingSystem class used for storing IoT devices’ state and metadata.

Voting

Para implements a simple voting mechanism for objects - each object can be voted up and down an has a votes property. Voting is useful for many application which require sorting by user votes. When a vote is cast, a new object of type Vote is created to store the vote in the database. Users have a configurable time window to amend their vote, they can no longer vote on that particular object.

User-defined objects

Let’s say you have an object of type Article in your application that you wish to persist. You can define your custom type through the REST API by issuing a simple POST request.

POST /v1/articles

{
 "appid": "myapp",
 "type": "article",
 "author": "Gordon Freeman"
}

You can create all sorts of objects with custom types and fields (properties) through the REST API. Note: When doing search on custom fields, add the “properties” prefix to them, like properties.myfield. Also keep in mind that the following are reserved words and they should not be used for naming your types (plural form included): “search(es)”, “util(s)”.

This creates a new Article and indexes all fields including the custom field author. To search for objects through the API, containing the author field we can do a request like this:

GET /v1/articles/search?q=properties.author:Gordon*

Note that we have q=properties.author:... instead of q=author:.... This is due to the fact that custom fields are stored in Para using a nested Map called properties (see the Sysprop class).

Resource permissions

When creating new users, we usually want to specify which resources they can access. This is why we added a few methods for adding and removing resource permissions. Each app can have any number of users and each user can have a set of permissions for a given resource. Resources are identified by name, for example the _batch resource would represent requests going to /v1/_batch.

There are several methods and flags which control which requests can go through. These are:

  • GET, POST, PUT, PATCH, DELETE - use these to allow a certain method explicitly
  • ? - use this to enable public (unauthenticated) access to a resource
  • - - use this to deny all access to a resource
  • * - wildcard, allow all request to go through
  • OWN - allow subject to only access objects they created

Let’s look at a few example scenarios where we give users permission to access the _batch. We have two users - user one with id = 1 and user two with id = 2. We’ll use the following methods:

boolean grantResourcePermission(String subjectid, String resourcePath, String[] permission);

boolean revokeResourcePermission(String subjectid, String resourcePath);

These methods allow the use of wildcards * for subjectid and resourcePath arguments.

Scenario 1: Give all users permission to READ - this allows them to make GET requests:

paraClient.grantResourcePermission("*", "_batch", ["GET"]);

Scenario 2: Give user 1 WRITE permissions - allow HTTP methods POST, PUT, PATCH, DELETE:

paraClient.grantResourcePermission("1", "_batch", ["POST", "PUT", "PATCH", "DELETE"]);

Also you could grant permissions on specific objects like so:

paraClient.grantResourcePermission(urlencode("user1"), urlencode("posts/123"), ["DELETE"]);

This will allow user1 to delete only the post object with an id of 123.

Scenario 3: Give user 2 permission ot only make POST requests:

paraClient.grantResourcePermission("2", "_batch", ["POST"]);

Note that all users still have the READ permissions because permissions are compounded. However, when grantResourcePermission() is called again on the same subject and resource, the new permission will overwrite the old one.

Scenario 4: Revoke all permissions for user 1 except READ:

paraClient.revokeAllResourcePermissions("1");

Scenario 5: Grant full access or deny all access to everyone:

paraClient.grantResourcePermission("*", "*", ["*"]);
paraClient.grantResourcePermission("*", "*", ["-"]);

Scenario 6: Grant full access to user 1 but only to the objects he/she created. In this case user 1 will be able to create, edit, delete and search todo objects but only those which he/she created, i.e. creatorid == 1.

paraClient.grantResourcePermission("1", "todo", ["*", "OWN"]);
paraClient.grantResourcePermission("1", "todo/*", ["*", "OWN"]);

To get all permissions for both users call:

paraClient.getAllResourcePermissions("1", "2");

The default initial policy for all apps is “deny all” which means that new users won’t be able to access any resources, except their own object and child objects, unless given explicit permission to do so.

You can also create “anonymous” permissions to allow unauthenticated users to access certain resources:

paraClient.grantResourcePermission("*", "public/resource", ["GET", "?"]);

This resource is now public but to access it you still need to specify your access key as a parameter:

GET /v1/public/resource?accessKey=app:myapp

Alternatively, on the client-side, you can set the Authorization header to indicate that the request is anonymous:

Authorization: Anonymous app:myapp

The special permission method ? means that anyone can do a GET request on public/resource.

To check if user 1 is allowed to access a particular resource call:

// returns 'false'
paraClient.isAllowedTo("1", "admin", "GET");

Validations

Para supports JSR-303 validation annotations but it also allows you to define validation constraints using the constraints API. This method is more flexible as it allows you to validate any property of any object.

The built-in constraints are:

required, min, max, size, email, digits, pattern, false, true, future, past, url.

Note: Objects are validated on create() and update() operations only.

User-defined validation constraints

Annotations work fine for most objects but are less useful when we want to define objects through the API. For this purpose we can use the constraints API:

paraClient.addValidationConstraint(String type, String field, Constraint c);

paraClient.removeValidationConstraint(String type, String field, String constraintName);

To create a constraint you can use the static methods provided by the Constraint class. For example calling Constraint.email() will return a new constraint object for checking email addresses.

We can use these methods to define constraints on custom types and fields that are not yet defined or are created by the API.

Integration with the client-side

You can easily implement client-side validation by getting the JSON object containing all validation constraints for all Para classes.

// return a JSON object with all validation constraints
paraClient.validationConstraints();

This returned JSON is in the following format (note that type names are all in lowercase):

'user': {
    'email': {
        'email': {
            'message': 'messages.email'
        },
        'required': {
            'message': 'messages.required'
        }
    },
    'identifier': {
        'required': {
            'message': 'messages.required'
        }
    }
    ...
},
...

This format used by the excellent JavaScript validation tool Valdr but can easily be integrated with other client-side validators.

Pager

The Pager class is used for pagination. It holds data about a page request. For example you can call a search method like this:

Pager pager = new Pager();
// limit results to 5
pager.setLimit(5);
// sort by the 'tag' field
pager.setSortby("tag");
// descending order
pager.setDesc(true);
List<Tag> tags = search.findTags("tag1", pager);
// the total number of tags for the query
int tagCount = pager.getCount();

Pager objects are used primarily in combination with search queries and allow you to limit the results of a query.

One-to-many

Object relationships are defined by the Linkable interface. All Para objects are linkable, meaning that they can be related to other Para objects.

Para supports one-to-many relationships between objects with the parentid field. It contains the id of the parent object. For example a user might be linked to their father like this:

+--------+
| Darth  |
| id: 5  |  Parent
+---+----+
    |
+---+---------+
| Luke        |
| id: 10      |  Child
| parentid: 5 |
+-------------+

This allows us to have a parent objects with many children which have the same parentid set. Now we can get all children for a given object by calling parent.getChildren(Class<P> clazz). This will return the list of objects that have a parentid equal to that object’s id. For example:

// assuming we have the parent object...
// this will return its first child of type User
User luke = client.getChildren(parent, "user").get(0);
User darth = client.read(luke.getParentid()); // parent

Many-to-many

Many-to-many relationships are implemented in Para with Linker objects. This object contains information about a link between two objects. This is simply the id and type of both objects. Linker objects are just regular Para objects - they can be persisted, indexed and cached.

+--------+
|  tag1  |
+---+----+
    |
+---+------------+
|post:10:tag:tag1|  Linker
+---+------------+
    |
+---+------+
|  Post1   |
|  id:10   |
+----------+

Note: The following methods are only used when creating “many-to-many” links. Linking and unlinking two objects, object1 and object2, is done like this:

client.link(object1, object2.getId());
client.unlink(object1, object2.getType(), object2.getId());
// delete all links to/from object1
client.unlinkAll(object1);

To check if two objects are linked use:

client.isLinked(object1, object2.getType(), object2.getId())

Also you can count the number of links by calling:

client.countLinks(object1, object2.getType())

Finally, to read all objects that are linked to object1, use:

client.getLinkedObjects(object1, object2.getType(), Pager... pager)

AWS IoT

Para is integrated with the AWS IoT cloud which means that you can create Thing objects through the Para API and have them sync their state with the AWS cloud. When you create a Thing it is also created on the AWS cloud and a certificate is generated for it which is saved within the Thing object. The certificate, private and public keys and other information about the device are only shown once the Thing is created. Here’s an example create request:

POST /v1/things
{
    "name": "MyThing",
    "serviceBroker": "AWS"
}

The response would be something like this:

{
  "id": "664870213421895680",
  "timestamp": 1468601996535,
  "type": "thing",
  "appid": "myapp",
  "name": "MyThing"
  "serviceBroker": "AWS",
  "deviceState": {},
  "deviceMetadata": {
    "thingId": "664870213421895680",
    "thingName": "MyThing",
    "thingARN": "arn:aws:iot:eu-west-1:123456789:thing/MyThing",
    "clientId": "MyThing",
    "clientCertId": "1abee5f25a5e1b1c66b66dd462dda32000621b429f6eb1ddb179ec3b",
    "clientCertARN": "arn:aws:iot:eu-west-1:123456789:cert/1abee5f25a5e1b1c66b66dd462dda32000621b429f6eb1ddb179ec3b",
    "clientCert": "-----BEGIN CERTIFICATE-----\n ... \n-----END CERTIFICATE-----\n",
    "privateKey": "-----BEGIN RSA PRIVATE KEY-----\n ... \n-----END RSA PRIVATE KEY-----\n",
    "publicKey": "-----BEGIN PUBLIC KEY-----\n ... \n-----END PUBLIC KEY-----\n",
    "region": "eu-west-1",
    "port": 8883,
    "host": "b2chw4axefhujt.iot.eu-west-1.amazonaws.com"
  }
}

Note that the field deviceDetails is only showed once and the device’s metadata is hidden on read. Now you can connect your device to the AWS cloud using the generated certificates and start sending messages with the state of the device. Para will check with AWS for shadow updates on each read and sync the Thing object’s state with the state that is stored in the cloud. If the Thing object is updated from the Para API, the AWS IoT shadow is updated accordingly so the two are in sync again. Finally, deleting the Thing object also deletes it from AWS along with the certificate and policy attached to it.

In order to use the IoT integration feature, Para needs to get your AWS credentials from the config file or from the instance it is deployed on.

Getting started with Para and AWS IoT

  1. Get an account from AWS and create a user with permissions to call the IoT API
  2. Set the para.aws_access_key and para.aws_secret_key properties in your Para config file
  3. Start the Para instance and create a Thing object through the API:
    POST /v1/things
    {
     "name": "myDevice",
     "serviceBroker": "AWS"
    }
    
  4. Take note of the deviceDetails field retured by this request as it contains certificates for your device
  5. Use the certificates to set up your device and connect it to AWS IoT
  6. The deviceState field inside the Para Thing object is synced with the device shadow on AWS on every GET request
  7. Update the deviceState from the Para API and it will be synced with the device shadow on AWS on every PATCH request.

Azure IoT

Our serice is hosted on AWS and the Azure integration is currently disabled. The reason for this is mainly to keep the costs down for our clients. We’re sorry for the inconvenience.

API Authentication

Para uses the AWS Signature Version 4 algorithm for signing API requests. We chose this algorithm instead of OAuth because it is less complicated and is already implemented inside the AWS Java SDK, which we have a direct dependency. In terms of security, both algorithms are considered very secure so there’s no compromise in that aspect.

Para offers two ways of authentication - one for apps using API keys and one for insecure clients (mobile, JS) using JWT. Apps authenticated with a secret key have full access to the API. Users authenticated with social login are issued JWT tokens and have limited access to the API, for example they can’t generate new API keys and they are authorized by specific resource permissions (see Resource permissions).

Full access for apps

In order to make a request to the API you need to have a pair of access and secret keys. Access keys are part of the HTTP request and secret keys are used for signing only and must be kept safe.

We recommend that you choose one of our API client libraries to handle the authentication for you.

Note: when a resource has public permissions you can access it without setting the Authorization header. Simply specify your access key as a parameter:

GET /v1/public/resource?accessKey=app:myapp

Changing keys

Call POST /v1/_newkeys to generate a new secret key (the request must be signed with the old keys).

For more information see the AWS documentation for REST authentication.

JSON Web Tokens - client access based on permissions

Para apps can create new users and grant them specific permissions by implementing social login (identity federation). First a user authenticates with their social identity provider such as Facebook, then comes back to Para with the access_token and is issued a new JSON Web Token that allows him to access the REST API.

JWT tokens are a new standard for authentication which is similar to cookies but is more secure, compact and stateless. An encoded token looks like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG
4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

When decoded the token looks like this:

// HEADER:
{
  "alg": "HS256",
  "typ": "JWT"
}
// PAYLOAD:
{
  "sub": "587408205806571520",
  "appid": "app:para",
    "refresh": 1450137214490,
  "nbf": 1450133614,
    "exp": 1450738414,
    "iat": 1450133614
}

To authenticate with users with social login use the Para client:

paraClient.signIn(String provider, String providerToken);

Supported providers are facebook, google, twitter, github, linkedin, microsoft, password, oauth2, ldap.

For example calling paraClient.signIn("facebook", "facebook_access_token") should return a new User object and would store the JWT token in memory. To get an access token from Facebook, use their JavaScript SDK.

After you call paraClient.signIn() and the request succeeds, the client caches the access token and all subsequent requests to the API will include that token until paraClient.signOut() is called. This is different from the normal operation using access and secret keys. Usually, tokens are used to authenticate users, unless they are “super” tokens, which can authenticate apps (see below).

To get or set access tokens use:

paraClient.getAccessToken();
paraClient.setAccessToken(String token);

To sign out and clear the JWT access token use:

paraClient.signOut();

Tokens can also be revoked by calling paraClient.revokeAllTokens() but this only works for authenticated users. The Para client takes care of refreshing the JWT tokens every hour and by default all tokens are valid for a week.

Creating “super” tokens

Since v1.18.3 we’ve added the support for “super” JSON web tokens. These are just like normal tokens for users, but instead of authenticating users we can authenticate apps with them. The give clients full access to the API, bypassing permissions. You don’t need to connect to a social identity provider like Facebook or Twitter - you simply generate the tokens on the client-side. Your code will need both the access key and secret key for this purpose.

For example, lets assume we have some JavaScript app running in the browser and we need admin access to our Para app. We could use the JavaScript client for Para but putting the secret key inside client-side code on the browser is not a smart move. So we pull in a library for generating JWT, like jsrsasign and we create the token ourselves. Here’s a snippet:

function getJWT(appid, secret) {
    var now = Math.round(new Date().getTime() / 1000);
    var sClaim = JSON.stringify({
        exp: now + (7 * 24 * 60 * 60), // expires at
        iat: now, // issued at
        nbf: now, // not valid before
        appid: appid // app id must be present
    });
    var sHeader = JSON.stringify({'alg': 'HS256', 'typ': 'JWT'});
    return KJUR.jws.JWS.sign(null, sHeader, sClaim, secret);
}

Your JS app could ask the user for the access keys, create a JWT and then discard the keys and use the newly generated “super” token. Once we have this we can attach it to every request as a header:

Authorization: Bearer eyJhbGciOiVCJ9.eyJzdWIiOi0._MKAH6SGiKSoqgwmqUaxuMyE

When calling the Para API from JavaScript in the browser, make sure you are running a web server and not as file:/// or your browser might not allow CORS requests.

Basic authentication

Para implements several authentication mechanisms which you can integrate in your application and make it easy to handle user registrations and logins.

The classic way of logging users in is with usernames and passwords. It takes a username or email and a password and tries to find that user in the database. If the user exists then it validates that the hash of the given password matches the hash in the database. The hashing algorithm is BCrypt.

The default URL for this filter is /password_auth and all requests to this location will be intercepted and processed by it. The default parameters to pass to it are email and password.

You can set for each app individually signin_success and signin_failure in the app’s settings, for controlling where should the user be redirected to on success or failure. For apps other than the root app use /password_auth?appid=myapp.

Here’s an example HTML form for initiating password-based authentication:

<form method="post" action="/password_auth">
    <input type="email" name="email">
    <input type="password" name="password">
    <input type="submit">
</form>

Creating users programmatically

You can create users from your Java code by using a ParaClient. By default, users are created with active = false, i.e. the account is locked until the email address is verified. To get around this, “verify” the user manually, like so:

// user is created but account is locked
paraClient.signIn("password", "user@example.com:Morgan Freeman:pass123");
// read identifier first to get the user id
ParaObject identifier = paraClient.read("user@example.com");
User user = paraClient.read(identifier.getCreatorid());
user.setActive(true);
User updated = paraClient.update(user); // user is now active

After executing the code above, any subsequent calls to paraClient.signIn() will be successful and the authenticated user object will be returned.

LDAP support

Users can be authenticated through an LDAP server, including Active Directory. The implementation uses the UnboundID SDK in combination with Spring Security. The user supplies a uid and password and Para connects to the LDAP server and tries to bind that user. Then, upon successful login, a new User object is created and the user is signed in. The user’s profile data (email, name, uid) is read from the LDAP directory. It is important to note that emails are not validated and are assumed valid.

The LDAP filter responds to requests at https://paraio.com/ldap_auth. The filter takes two query parameters username and password and answers to any HTTP method. Example: GET https://paraio.com/ldap_auth?username=bob&password=secret

These are the configuration options for this filter:

propertydescription
security.ldap.server_urlURL of the LDAP server, including scheme and port, defaults to ldap://localhost:8389/.
security.ldap.base_dnThe base DN, aka domain (default is dc=springframework,dc=org).
security.ldap.bind_dnThe initial bind DN for a user with search privileges (default is blank).
security.ldap.bind_passThe password for a user with search privileges (default is blank).
security.ldap.user_search_baseSearch base for user searches (default is blank).
security.ldap.user_search_filterSearch filter for user searches (default is (cn={0})).
security.ldap.user_dn_patternDN pattern for finding users directly (default is uid={0},ou=people).
security.ldap.password_attributeThe password attribute in the directory (default is userPassword).
security.ldap.active_directory_domainThe domain name for AD server. (default is blank, AD is disabled).
security.ldap.compare_passwordsIf set to any value, will switch to password comparison strategy instead of default “bind” method. (default - not set).

Note: Active Directory support is enabled when active_directory_domain is set.

You can set those LDAP properties through the app settings API:

{
    "security.ldap.server_url": "ldap://localhost:8389/",
    "security.ldap.base_dn": "dc=springframework,dc=org",
    "security.ldap.bind_dn": "admin",
    "security.ldap.bind_pass": "secret",
    ...
    "signin_success": "http://success.url",
    "signin_failure": "http://failure.url"
}

OpenID support

Note: OpenID 2.0 is no longer supported by Google, and we recommend using OAuth or JWT-based authentication.

The default URL for this filter is /openid_auth.

The filter takes a request with the openid_identifier parameter and redirects the user to their OpenID provider for verification. Once the user’s identity is verified, the provider redirects the user back to our filter and the user gets either logged in or registered.

You can configure the URLs for authentication success and failure in the configuration file (see the config)

Facebook support

This describes the web authentication flow with Facebook. You could also login with an existing access token from Facebook through the API. This web flow sets a cookie, the API returns a JWT instead.

First of all you need to have your API credentials ready by creating an app in the Facebook Dev Center. Then set these through the app settings API:

{
    "fb_app_id": "..."
    "fb_secret": "..."
    "signin_success": "http://success.url"
    "signin_failure": "http://failure.url"
}

If you want Para to generate a JWT token upon successful authentication, add the jwt=? parameter to your signin_success url. For example { "signin_success": "http://success.url?jwt=?" }. Para will redirect the user back to your host URL with the generated access token.

This authentication filter responds to requests at https://paraio.com/facebook_auth.

To initiate a login with Facebook just redirect the user to the Facebook OAuth endpoint:

facebook.com/dialog/oauth

Pass the parameter redirect_uri=/facebook_auth?appid=myapp so Para can handle the response from Facebook.

Note: You need to register a new application with Facebook in order to obtain an access and secret keys.

Below is an example Javascript code for a Facebook login button:

$("#facebookLoginBtn").click(function() {
        window.location = "https://www.facebook.com/dialog/oauth?" +
                "response_type=code&client_id={FACEBOOK_APP_ID}" +
                "&scope=email&state=" + (new Date().getTime()) +
                "&redirect_uri=https://paraio.com/facebook_auth?appid=myapp";
        return false;
});

GitHub support

This describes the web authentication flow with GitHub. You could also login with an existing access token from GitHub through the API. This web flow sets a cookie, the API returns a JWT instead.

First of all you need to have your API credentials ready by creating an app on GitHub. Then set these through the app settings API:

{
    "gh_app_id": "..."
    "gh_secret": "..."
    "signin_success": "http://success.url"
    "signin_failure": "http://failure.url"
}

If you want Para to generate a JWT token upon successful authentication, add the jwt=? parameter to your signin_success url. For example { "signin_success": "http://success.url?jwt=?" }. Para will redirect the user back to your host URL with the generated access token.

This authentication filter responds to requests at https://paraio.com/github_auth.

To initiate a login with GitHub just redirect the user to the GitHub OAuth endpoint:

github.com/login/oauth/authorize

Pass the parameter redirect_uri=/github_auth?appid=myapp so Para can handle the response from GitHub.

Note: You need to register a new application with GitHub in order to obtain an access and secret keys.

Below is an example Javascript code for a GitHub login button:

$("#githubLoginBtn").click(function() {
        window.location = "https://github.com/login/oauth/authorize?" +
                "response_type=code&client_id={GITHUB_APP_ID}" +
                "&scope=user&state=" + (new Date().getTime()) +
                "&redirect_uri=https://paraio.com/github_auth?appid=myapp";
        return false;
});

Google support

This describes the web authentication flow with Google. You could also login with an existing access token from Google through the API. This web flow sets a cookie, the API returns a JWT instead.

First of all you need to have your API credentials ready by creating an app in the Google Dev Console. Then set these through the app settings API:

{
    "gp_app_id": "..."
    "gp_secret": "..."
    "signin_success": "http://success.url"
    "signin_failure": "http://failure.url"
}

If you want Para to generate a JWT token upon successful authentication, add the jwt=? parameter to your signin_success url. For example { "signin_success": "http://success.url?jwt=?" }. Para will redirect the user back to your host URL with the generated access token.

This authentication filter responds to requests at https://paraio.com/google_auth.

To initiate a login with Google just redirect the user to the Google OAuth endpoint:

accounts.google.com/o/oauth2/v2/auth

Pass the parameter redirect_uri=/google_auth?appid=myapp so Para can handle the response from Google.

Note: You need to register a new application with Google in order to obtain an access and secret keys.

Below is an example Javascript code for a Google login button:

$("#googleLoginBtn").click(function() {
        var baseUrl = window.location.origin;
        window.location = "https://accounts.google.com/o/oauth2/v2/auth?" +
                "client_id={GOOGLE_APP_ID}&response_type=code" +
                "&scope=openid%20email&redirect_uri=https://paraio.com/google_auth?appid=myapp" +
                "&state=" + (new Date().getTime()) + "&" + "openid.realm=" + baseUrl;
        return false;
});

LinkedIn support

This describes the web authentication flow with LinkedIn. You could also login with an existing access token from LinkedIn through the API. This web flow sets a cookie, the API returns a JWT instead.

First of all you need to have your API credentials ready by creating an app on LinkedIn. Then set these through the app settings API:

{
    "in_app_id": "..."
    "in_secret": "..."
    "signin_success": "http://success.url"
    "signin_failure": "http://failure.url"
}

If you want Para to generate a JWT token upon successful authentication, add the jwt=? parameter to your signin_success url. For example { "signin_success": "http://success.url?jwt=?" }. Para will redirect the user back to your host URL with the generated access token.

This authentication filter responds to requests at https://paraio.com/linkedin_auth.

To initiate a login with LinkedIn just redirect the user to the LinkedIn OAuth endpoint:

linkedin.com/uas/oauth2/authorization

Pass the parameter redirect_uri=/linkedin_auth?appid=myapp so Para can handle the response from LinkedIn.

Note: You need to register a new application with LinkedIn in order to obtain an access and secret keys.

Below is an example Javascript code for a LinkedIn login button:

$("#linkedinLoginBtn").click(function() {
        window.location = "https://www.linkedin.com/uas/oauth2/authorization?" +
                "response_type=code&client_id={LINKEDIN_APP_ID}" +
                "&scope=r_emailaddress&state=" + (new Date().getTime()) +
                "&redirect_uri=https://paraio.com/linkedin_auth?appid=myapp";
        return false;
});

Microsoft support

This describes the web authentication flow with Microsoft. You could also login with an existing access token from Microsoft through the API. This web flow sets a cookie, the API returns a JWT instead.

First of all you need to have your API credentials ready by creating an app on Microsoft. Then set these through the app settings API:

{
    "ms_app_id": "..."
    "ms_secret": "..."
    "signin_success": "http://success.url"
    "signin_failure": "http://failure.url"
}

If you want Para to generate a JWT token upon successful authentication, add the jwt=? parameter to your signin_success url. For example { "signin_success": "http://success.url?jwt=?" }. Para will redirect the user back to your host URL with the generated access token.

This authentication filter responds to requests at https://paraio.com/microsoft_auth.

To initiate a login with Microsoft just redirect the user to the Microsoft OAuth endpoint:

login.microsoftonline.com/common/oauth2/v2.0/authorize

Pass the parameter redirect_uri=/microsoft_auth so Para can handle the response from Microsoft. Note: The v2.0 endpoint for OAuth authentication with Microsoft doesn’t work well with query parameters in the redirect_uri parameter. Instead, use the state parameter to remember the appid of your app (see example below).

Note: You need to register a new application with Microsoft in order to obtain an access and secret keys.

Below is an example Javascript code for a Microsoft login button:

$("#microsoftLoginBtn").click(function() {
        window.location = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?" +
                "response_type=code&client_id={MICROSOFT_APP_ID}" +
                "&scope=https%3A%2F%2Fgraph.microsoft.com%2Fuser.read&state={YOUR_APP_ID}" +
                "&redirect_uri=https://paraio.com/microsoft_auth";
        return false;
});

Twitter support

This describes the web authentication flow with Twitter. You could also login with an existing access token from Twitter through the API. This web flow sets a cookie, the API returns a JWT instead.

First of all you need to have your API credentials ready by creating an app on Twitter. Then set these through the app settings API:

{
    "tw_app_id": "..."
    "tw_secret": "..."
    "signin_success": "http://success.url"
    "signin_failure": "http://failure.url"
}

If you want Para to generate a JWT token upon successful authentication, add the jwt=? parameter to your signin_success url. For example { "signin_success": "http://success.url?jwt=?" }. Para will redirect the user back to your host URL with the generated access token.

This authentication filter responds to requests at https://paraio.com/twitter_auth.

To initiate a login with Twitter just redirect the user to the https://paraio.com/twitter_auth?appid=myapp. This will redirect the user to Twitter for authentication.

Note: You need to register a new application with Twitter in order to obtain an access and secret keys. The Twitter API does not share users’ emails by default, you have to ask Twitter to whitelist your app first.

Below is an example Javascript code for a Twitter login button:

$("#twitterLoginBtn").click(function() {
        window.location = "https://paraio.com/twitter_auth?appid=myapp";
        return false;
});

API methods

All API methods below require authentication by default, unless it’s written otherwise.

Limiting which fields are returned by the API

Field limiting is supported on all requests by using the query parameter select=xxx,yyy. This parameter takes a comma separated list of fields to include. For example:

GET /myobjects?select=id,name,custom_field1,custom_field2



▾▴ collapse/expand all

POST /jwt_auth Sign in (JWT)

Takes an identity provider access token and fetches the user data from that provider. A new User object is created if that user doesn’t exist and is then returned. Access tokens are returned upon successful authentication using one of the SDKs from Facebook, Google, Twitter, etc.

Note: Twitter uses OAuth 1 and gives you a token and a token secret so you must concatenate them first - {oauth_token}:{oauth_token_secret}, and then use that as the provider access token. Also if you use the password provider, the token parameter must be in the format {email}:{full_name}:{password} or {email}::{password} (must be :: if name is empty). For LDAP the token looks similar - {uid}:{password} (single :).

Also keep in mind that when a new user signs in with a password and unverified email, through /jwt_auth, Para will create the user but will return an error 400 indicating that the user is not active and cannot be authenticated. Once the email is verified and the user is set to active: true, subsequent sign in attempts will be successful.

Request

  • body - a JSON object containing appid, provider and token properties (required).

Request body example for authenticating with email and password:

{
    "appid": "app:myapp",
    "provider": "password",
    "token": "user@domain.com::password123"
}

Request body example for Facebook:

{
    "appid": "app:myapp",
    "provider": "facebook",
    "token": "eyJhbGciOiJIUzI1NiJ9.eWIiO..."
}

The appid is the id of your own app that you’re trying to sign in to. The provider field a string and can be one of the following values:

  • facebook - sign in with Facebook account,
  • google - sign in with Google account,
  • twitter - sign in with Twitter account,
  • github - sign in with GitHub account,
  • linkedin - sign in with LinkedIn account,
  • microsoft - sign in with Microsoft account,
  • password - sign in with email and password.
  • oauth2 - sign in with generic OAuth 2.
  • ldap - sign in with LDAP.

Response

Returns a JSON object containing JWT properties and a User object. The returned JWT properties are:

  • access_token - the JWT access token.
  • expires - a Java timestamp of when the token will expire.
  • refresh - a Java timestamp indicating when API clients should refresh their tokens, usually 1 hour after token has been issued.

  • status codes - 200, 400

Example response:

{
    "jwt": {
        "access_token": "eyJhbGciOiJIUzI1NiJ9.eyJ...",
        "expires": 1450137214490,
        "refresh": 1450137216490
    },
    "user": {
        "id":"user1",
        "timestamp": 1399721289987,
        "type":"user",
        "appid":"myapp",
        ...
    }
}

GET /jwt_auth Token refresh (JWT)

Refreshes the access token if and only if the provided token is valid and not expired. Tokens should be refreshed periodically in order to keep users logged in for longer periods of time.

Request

Request should include an Authorization: Bearer {JWT_TOKEN} header containing a valid access token. (required) As an alternative you could provide the token as query parameter instead of a header.

Response

Returns a JSON object containing a new JWT token and the same User object. The returned JWT properties are:

  • access_token - the JWT access token.
  • expires - a Java timestamp of when the token will expire.
  • refresh - a Java timestamp indicating when API clients should refresh their tokens.

  • status codes - 200, 400

Example response:

{
    "jwt": {
        "access_token": "eyJhbGciOiJIUzI1NiJ9.eyJ...",
        "expires": 1450137214490,
        "refresh": 1450137216490
    },
    "user": {
        "id":"user1",
        "timestamp": 1399721289987,
        "type":"user",
        "appid":"myapp",
        ...
    }
}

DELETE /jwt_auth Revoke all tokens (JWT)

Revokes all user tokens for the user that is currently logged in. This would be equivalent to “logout everywhere”. Note: Generating a new API secret on the server will also invalidate all client tokens.

Request

Request should include an Authorization: Bearer {JWT_TOKEN} header containing a valid access token. (required) As an alternative you could provide the token as query parameter instead of a header.

Response

Returns a JSON object containing a new JWT token and the same User object. The returned JWT properties are:

  • access_token - the JWT access token.
  • expires - a Java timestamp of when the token will expire.
  • refresh - a Java timestamp indicating when API clients should refresh their tokens.

  • status codes - 200, 400

Example response (response is empty):

200 OK

POST /v1/{type} Create objects

Creates new objects of type {type}. You can also create objects with custom types and fields (since v1.4.0).

Request

  • body - the JSON object to create
  • {type} - the plural form of the object’s type, e.g. “users”

Example request body:

{
    "type":"tag",
    "plural":"tags",
    "tag":"tag1"
}

Notice how the type field is in singular form and the plural field is the plural form of the type’s name. These are required for mapping types to URLs.

Response

  • status codes - 201, 400

Example response for a Tag:

{
    "id":"tag:tag1",
    "timestamp":1399721289987,
    "type":"tag",
    "appid":"para",
    "name":"tag tag:tag1",
    "votes":0,
    "plural":"tags",
    "objectURI":"/tags/tag1",
    "tag":"tag1",
    "count":0
}

GET /v1/{type}/{id} Read objects

Returns objects of type {type}.

Request

  • {type} - the plural form of the object’s type, e.g. “users”
  • {id} - the id

Note: If {id} is omitted then the response will be a list of all objects of the specified type.

Response

  • status codes - 200, 404

Example response for a Tag:

{
    "id":"tag:tag1",
    "timestamp":1399721289987,
    "type":"tag",
    "appid":"para",
    "name":"tag tag:tag1",
    "votes":0,
    "plural":"tags",
    "objectURI":"/tags/tag1",
    "tag":"tag1",
    "count":0
}

PUT /v1/{type}/{id} Overwrite objects

Overwrites an object of type {type}. If the object with this {id} doesn’t exist then a new one will be created.

Request

  • body - the JSON data to merge with the stored object
  • {type} - the plural form of the object’s type, e.g. “users”
  • {id} - the id

Response

  • status codes - 200, 400, 404, 500

Example response for a Tag with updated count:

{
    "id":"tag:tag1",
    "timestamp":1399721289987,
    "type":"tag",
    "appid":"para",
    "name":"tag tag:tag1",
    "votes":0,
    "plural":"tags",
    "objectURI":"/tags/tag1",
    "tag":"tag1",
    "count":55
}

PATCH /v1/{type}/{id} Update objects

Updates objects of type {type}. Partial objects are supported.

Vote requests: these are a special kind of PATCH request, which has a body like {"_voteup": "user123"} or {"_votedown": "user123"}. Here user123 is the id of the voter. A successful vote request either increments or decrements the votes field by 1.

Request

  • body - the JSON object to merge with the stored object OR a vote request body like {"_voteup": "user123"}
  • {type} - the plural form of the object’s type, e.g. “users”
  • {id} - the id

Response

  • status codes - 200, 400, 404, 500, vote requests return true or false

Example response for a Tag with updated count:

{
    "id":"tag:tag1",
    "timestamp":1399721289987,
    "type":"tag",
    "appid":"para",
    "name":"tag tag:tag1",
    "votes":0,
    "plural":"tags",
    "objectURI":"/tags/tag1",
    "tag":"tag1",
    "count":55
}

DELETE /v1/{type}/{id} Delete objects

Deletes objects of type {type}.

Request

  • {type} - the plural form of the object’s type, e.g. “users”
  • {id} - the id

Response

  • status codes - 200, 400

No content.

POST /v1/_batch Batch create

Creates multiple objects with a single request. PUT requests to this resource are equivalent to POST.

Request

  • body - a JSON array of objects to create (required).

Maximum request size is 1 megabyte.

Response

  • status codes - 200, 400

Example response for creating 3 objects (returns a list of the created objects):

[ { "id":"id1", ... }, { "id":"id2", ... }, { "id":"id3", ... } ]

GET /v1/_batch Batch read

Returns a list of objects given their id fields.

Parameters

  • ids - a list of ids of existing objects (required).

Example: GET /v1/_batch?ids=id1&ids=id2&ids=id3

Response

  • status codes - 200, 400 (if no ids are specified)

Example response for reading 3 objects:

[ { "id":"id1", ... }, { "id":"id2", ... }, { "id":"id3", ... } ]

PATCH /v1/_batch Batch update

Updates multiple objects with a single request. Partial objects are supported. Note: These objects will not be validated as this would require us to read them first and validate them one by one.

Request

  • body - a JSON array of objects to update (required). The fields id and type are required for each object.

Maximum request size is 1 megabyte.

Response

  • status codes - 200, 400

Example response for updating 3 objects (returns a list of the updated objects):

[ {
    "id":"id1",
    "type":"type1",
    "name":"newName1", ...
  }, {
    "id":"id2",
    "type":"type2",
    "name":"newName2", ...
  }, {
     "id":"id3",
     "type":"type3",
     "name":"newName3", ...
} ]

DELETE /v1/_batch Batch delete

Deletes multiple objects with a single request.

Parameters

  • ids - a list of ids of existing objects (required).

Example: DELETE /v1/_batch?ids=1&ids=2 will delete the two objects with an id of 1 and 2, respectively.

Response

  • status codes - 200, 400 (if request maximum number of ids is over the limit of ~30)

No content.

GET /v1/{type} Basic search

Searches for objects of type {type}.

Note: Requests to this path and /v1/{type}/search/{querytype} are handled identically. Also, note that custom fields must be used in search queries as properties.myfield.

For detailed syntax of the query string see Lucene’s query string syntax.

Request

  • {type} - the plural form of the object’s type, e.g. “users”
  • {querytype} - the type of query to execute (optional, see Search)

Parameters

  • q - a search query string (optional). Defaults to * (all).
  • desc - sort order - true for descending (optional). Default is true.
  • sort - the field to sort by (optional).
  • limit - the number of results to return. Default is 30.
  • page - starting page for results (optional). (note: page size is 30 items by default)

Response

  • status codes - 200

Example response for querying all tags GET /v1/tags?q=*&limit=3:

{
    "page":0,
    "totalHits":3,
    "items":[{
        "id":"tag:tag3",
        "timestamp":1400077389250,
        "type":"tag",
        "appid":"para",
        "name":"tag tag:tag3",
        "votes":0,
        "plural":"tags",
        "objectURI":"/tags/tag3",
        "tag":"tag3",
        "count":0
    }, {
        "id":"tag:tag1",
        "timestamp":1400077383588,
        "type":"tag",
        "appid":"para",
        "name":"tag tag:tag1",
        "votes":0,
        "plural":"tags",
        "objectURI":"/tags/tag1",
        "tag":"tag1",
        "count":0
    }, {
        "id":"tag:tag2",
        "timestamp":1400077386726,
        "type":"tag",
        "appid":"para",
        "name":"tag tag:tag2",
        "votes":0,
        "plural":"tags",
        "objectURI":"/tags/tag2",
        "tag":"tag2",
        "count":0
    }]
}

GET /v1/search/{querytype} Advanced search

Executes a search query.

Note: custom fields must be used in search queries as properties.myfield.

Request

  • {querytype} - the type of query to execute (optional), use one of types below:

The querytype parameter switches between the different query types. If this parameter is missing then the generic findQuery() method will be executed by default.

id query

Finds the object with the given id from the index or null. This executes the method findById() with these parameters:

  • id - the id to search for

ids query

Finds all objects matchings the given ids. This executes the method findByIds() with these parameters:

  • ids - a list of ids to search for

nested query

Searches through objects in a nested field named nstd. Used internally for joining search queries on linked objects. This executes the method findNestedQuery() with these parameters:

  • q - a search query string
  • field - the name of the field to target within a nested object
  • type - a type to search for

nearby query

Location-based search query. Relies on Address objects for coordinates. This executes the method findNearby() with these parameters:

  • latlng - latitude and longitude of the center of the search perimeter
  • radius - radius of the search perimeter in kilometers.

prefix query

Searches for objects containing a field (property) that starts with the given prefix. This executes the method findPrefix() with these parameters:

  • field - the field to search on
  • prefix - the prefix

similar query

“More like this” search query. This executes the method findSimilar() with these parameters:

  • fields - a list of fields, for example:
    GET /v1/search/similar?fields=field1&fields=field2
    
  • filterid - an id filter; excludes a particular object from the results
  • like - the source text to use for comparison

tagged query

Search for objects tagged with a set of tags. This executes the method findTagged() with these parameters:

  • tags - a list of tags, for example:
    GET /v1/search/tagged?tags=tag1&tags=tag2
    

in query

Searches for objects containing any of the terms in the given list (matched exactly). This executes the method findTermInList() with these parameters:

  • field - the field to search on
  • terms - a list of terms (values)

terms query

Searches for objects containing all of the specified terms (matched exactly) This executes the method findTerms() with these parameters:

  • matchall - if true executes an AND query, otherwise an OR query
  • terms - a list of field:term pairs, for example:
    GET /v1/search/terms?terms=field1:term1&terms=field2:term2
    
  • count - if present will return 0 objects but the “totalHits” field will contain the total number of results found that match the given terms.

Since v1.9, the terms query supports ranges. For example if you have a pair like 'age':25 and you want to find objects with higher age value, you can modify the key to have a relational operator 'age >':25. You can use the >, <, >=, <= operators by appending them to the keys of the terms map.


wildcard query

A wildcard query like “example“. This executes the method findWildcard() with these *parameters:

  • field - the field to search on

count query

Returns the total number of results that would be returned by a query. This executes the method getCount() with no parameters.

Request parameters

  • q - a search query string (optional). Defaults to * (all).
  • type - the type of objects to search for (optional). prefix, tagged, in, terms, wildcard, count. (see Search)
  • desc - sort order - true for descending (optional). Default is true.
  • sort - the field to sort by (optional).
  • page - starting page for results (optional). (note: page size is 30 items by default)
  • limit - the number of results to return

Response

  • status codes - 200

Example response for counting all objects (just three for this example):

{
    "page":0,
    "totalHits":3,
    "items":[
    ]
}

GET /v1/{type}/{id}/links/{type2}/{id2} Find linked objects

Call this method to search for objects that linked to the object with the given {id}.

Note: When called with the parameter ?childrenonly, the request is treated as a “one-to-many” search request. It will do asearch for child objects directly connected to their parent by the parentid field. Without ?childrenonly the request is treated as a “many-to-many” search request.

Request

  • {type} - the type of the first object, e.g. “users”
  • {id} - the id of the first object
  • {type2} - the type of the second object (required)
  • {id2} - the id field of the second object (optional)

Parameters

  • childrenonly - if set and {id2} is not set, will return a list of child objects (these are the objects with parentid equal to {id} above). Also if field and term parameters are set, the results are filtered by the specified field and the value of that field (term).
  • count - if set will return no items an the total number of linked objects. If childrenonly is set, this will return only the count of child objects.
  • q - query string, if set, all linked/child objects will be searched and those that match the query are returned. To search only child objects that are linked by parentid use q in combination with childrenonly, otherwise the Linker objects will be searched. Since v1.19 Linker objects contain a copy of the two objects they connect. This enables Para to execute more complex “joined” search queries. The effectiveness of these is determined by how up-to-date the data inside a Linker is.

Response

  • If the {id2} parameter is specified, the response will be a boolean text value - true if objects are linked.
  • If the {id2} parameter is missing, the response will be a list of linked objects. (pagination parameters are applicable)
  • childrenonly - if set, the response will be a list of child objects (pagination parameters are applicable)

  • status codes - 200, 400 (if type parameter is missing)

Example response if id is missing:

{
    "page":X,
    "totalHits":Y,
    "items":[
        ...
    ]
}

Response if id is specified: true or false

POST /v1/{type}/{id}/links/{id2} Link objects

This will link the object with {id} to another object with the specified id in the id parameter. The created link represents a many-to-many relationship (see also one-to-many relationships).

Don’t use this method for “one-to-many” links. Creating one-to-many links is trivial - just set the parentid of an object (child) to be equal to the id field of another object (parent).

PUT requests to this resource are equivalent to POST.

Request

  • {type} - the type of the first object, e.g. “users”
  • {id} - the id of the first object
  • {id2} - the id field of the second object (required)

Response

Returns the id of the Linker object - the linkId - which contains the types and ids of the two objects.

  • status codes - 200, 400 (if any of the parameters are missing)

Example response:

"type1:id1:type2:id2"

DELETE /v1/{type}/{id}/links/{type2}/{id2} Unlink objects

Unlinks or deletes the objects linked to the object with the specified {id}.

Request

  • {type} - the type of the first object, e.g. “users”
  • {id} - the id of the first object
  • {type2} - the type of the second object (not required, if this and {id2} are missing, it will unlink everything)
  • {id2} - the id field of the second object (optional)

Parameters

  • all - setting this will delete all linked objects (be careful!)
  • childrenonly - if set, all child objects will be deleted rather than unlinked (be careful!)

Note:

  • If both {type2} and {id2} are not set, all linked objects will be unlinked from this one.
  • If id is set - the two objects are unlinked.
  • If all and id are not set, but childrenonly is set then the child objects with type type are deleted! (these are the objects with parentid equal to {id} above)

Response

  • status codes - 200

No content.

PUT /v1/_constraints/{type}/{field}/{cname} Add constraint

Adds a new validation constraint to the list of constraints for the given field and type.

Request

  • body - the JSON payload of the constraint (see the table below)
  • {type} - the object type to which the constraint applies (required)
  • {field} - the name of the field to which the constraint applies (required)
  • {cname} - the constraint name (required), one of the following:

    NamePayload (example)
    requirednone
    emailnone
    falsenone
    truenone
    pastnone
    presentnone
    urlnone
    min{ "value": 123 }
    max{ "value": 123 }
    size{ "min": 123, "max": 456 }
    digits{ "integer": 4, "fraction": 2 }
    pattern{ "value": "^[a-zA-Z]+$" }

Response

Returns a JSON object containing the validation constraints for the given type.

  • status codes - 200, 400

Example response:

{
    "User" : {
        "identifier" : {
            "required" : {
                "message" : "messages.required"
            }
        },
        "groups" : {
            "required" : {
                "message" : "messages.required"
            }
        },
        "email" : {
            "required" : {
                "message" : "messages.required"
            },
            "email" : {
                "message" : "messages.email"
            }
        }
    },
    ...
}

GET /v1/_constraints/{type} List constraints

Returns an object containing all validation constraints for all defined types in the current app. This information can be used to power client-side validation libraries like valdr.

Request

  • {type} - when supplied, returns only the constraints for this type (optional). If this parameter is omitted, all constraints for all types will be returned.

Response

Returns a JSON object with all validation constraints for a given type. The message field is a key that can be used to retrieve a localized message.

  • status codes - 200

Example response:

{
    "User" : {
        "identifier" : {
            "required" : {
                "message" : "messages.required"
            }
        },
        "groups" : {
            "required" : {
                "message" : "messages.required"
            }
        },
        "email" : {
            "required" : {
                "message" : "messages.required"
            },
            "email" : {
                "message" : "messages.email"
            }
        }
    },
    ...
}

DELETE /v1/_constraints/{type}/{field}/{cname} Remove constraint

Removes a validation constraint from the list of constraints for the given field and type.

Request

  • {type} - the object type to which the constraint applies (required)
  • {field} - the name of the field to which the constraint applies (required)
  • {cname} - the constraint name (required, see the table above)

Response

Returns a JSON object containing the validation constraints for the given type.

  • status codes - 200, 400

Example response:

{
    "User" : {
        "identifier" : {
            "required" : {
                "message" : "messages.required"
            }
        },
        "groups" : {
            "required" : {
                "message" : "messages.required"
            }
        },
        "email" : {
            "required" : {
                "message" : "messages.required"
            },
            "email" : {
                "message" : "messages.email"
            }
        }
    },
    ...
}

GET /v1/_id/{id} Read by id

Returns the object for the given {id}.

Request

  • {id} - the id

Response

Returns a JSON object.

  • status codes - 200, 404

Example response:

{
    "id" : "417283630780387328",
  "timestamp" : 1409572755025,
  "type" : "user",
    "name" : "Gordon Freeman"
    ...
}

GET /v1/_me Me object

Returns the currently authenticated User or App object. If the request is unauthenticated 401 error is returned.

Request

No parameters.

Response

Returns the JSON object for the authenticated User or App.

  • status codes - 200, 401

Example response:

{
    "id" : "417283630780387328",
  "timestamp" : 1409572755025,
  "type" : "user",
    "name" : "Gordon Freeman"
    ...
}

GET /v1/_types List types

Returns a list of all known types for this application, including core types and user-defined types. User-defined types are custom types which can be defined through the REST API and allow the users to call the standard CRUD methods on them as if they were defined as regular Para objects. See User-defined classes for more details.

Request

No parameters.

Response

Returns a list of all types that are defined for this application.

  • status codes - 200

Example response for querying all types:

[
    "addresses":"address",
    "apps":"app",
    "sysprops":"sysprop",
    "tags":"tag",
    "translations":"translation",
    "users":"user",
    "votes":"vote"
]

GET /v1/_permissions/{subjectid}/{resource}/{method} Check permission

This checks if a subject is allowed to execute a specific type of request on a resource.

There are several methods and flags which control which requests can go through. These are:

  • GET, POST, PUT, PATCH, DELETE - use these to allow a certain method explicitly
  • ? - use this to enable public (unauthenticated) access to a resource
  • - - use this to deny all access to a resource
  • * - wildcard, allow all request to go through
  • OWN - allow subject to only access objects they created

Request

  • {subjectid} - the subject/user id to grant permissions to. (required)
  • {resource} - the resource path or object type (URL encoded). (required)
  • {method} - an HTTP method or flag, listed above. (required)

Response

Returns a boolean plain text response - true or false.

  • status codes - 200, 400, 404

Example response:

true

GET /v1/_permissions/{subjectid} List permissions

Returns a permissions objects containing all permissions. If {subjectid} is provided, the returned object contains only the permissions for that subject.

Request

  • {subjectid} - the subject/user id (optional)

Response

Returns a JSON object containing the resource permissions for the given user.

  • status codes - 200, 400, 404

Example response:

{
  "*": {
        "*": ["GET"]
    },
  "user1": [],
  "user2": {
        "posts": ["GET", "POST"]
    },
  "user3": {
    "*": ["*"]
  }
}

PUT /v1/_permissions/{subjectid}/{resource} Grant permissions

Grants a set of permissions (allowed HTTP methods) to a subject for a given resource.

There are several methods and flags which control which requests can go through. These are:

  • GET, POST, PUT, PATCH, DELETE - use these to allow a certain method explicitly
  • ? - use this to enable public (unauthenticated) access to a resource
  • - - use this to deny all access to a resource
  • * - wildcard, allow all request to go through
  • OWN - allow subject to only access objects they created

Request

  • body - a JSON array of permitted HTTP methods/flags, listed above (required).
  • {subjectid} - the subject/user id to grant permissions to (required)
  • {resource} - the resource path or object type (URL encoded), for example posts corresponds to /v1/posts, posts%2F123 corresponds to /v1/posts/123 (required)

Response

Returns a JSON object containing the resource permissions for the given user.

  • status codes - 200, 400, 404

Example response:

{
  "user2": {
        "posts": ["GET", "POST"]
    }
}

DELETE /v1/_permissions/{subjectid}/{resource} Revoke permissions

Revokes all permissions for a given subject and resource. If {resource} is not specified, revokes every permission that has been granted to that subject.

Request

  • {subjectid} - the subject/user id to grant permissions to (required)
  • {resource} - the resource path or object type (URL encoded). If omitted, all permissions for that subject will be revoked. (optional)

Response

Returns a JSON object containing the resource permissions for the given user.

  • status codes - 200, 400, 404

Example response:

{
  "user1": [],
}

GET /v1/_settings List custom settings

Lists all custom app settings. These can be user-defined key-value pairs and are stored withing the app object.

Request

No parameters.

Response

Returns an map of keys and values.

  • status codes - 200

Example response:

{
    "fb_app_id": "123U3VTNifLPqnZ1W2",
    "fb_secret": "YXBwOnBhcmE11234151667",
    "signin_success": "/dashboard",
    "signin_failure": "/signin?error"
}

PUT /v1/_settings/{key} Add custom setting

Adds a new custom app setting or overwrites an existing one. To overwrite all app settings, make a PUT request without providing the key parameter, like so:

PUT /v1/_settings
{
    "fb_app_id": "123U3VTNifLPqnZ1W2",
    "fb_secret": "YXBwOnBhcmE11234151667",
    "signin_success": "/dashboard",
    "signin_failure": "/signin?error"
}

This will replace all app-specific settings with the JSON object in that request.

Request

  • body - a JSON object with a single value field: { "value": "setting_value" }, or an object containing all app-specific configuration properties.
  • {key} - a key from the settings map (optional). If {key} is missing, all app settings will be overwritten by the JSON in the body of the request.

Response

Returns an empty response.

  • status codes - 200

POST /v1/_newkeys Reset API keys

This will reset your API secret key by generating a new one. Make sure you save it and use it for signing future requests.

Request

No parameters.

Response

Returns the access and secret keys for this application which will be used for request signing.

  • status codes - 200

Example response:

{
    "secretKey": "U3VTNifLPqnZ1W2S3pVVuKG4HOVbimMocdDMl8T69BB001AXGZtwZw==",
    "accessKey": "YXBwOnBhcmE=",
    "info": "Save the secret key! It is showed only once!"
}

GET /v1/utils/{method} Utilities

Utility functions which can be accessed via the REST API include (listed by the method’s short name):

  • newid - calls Utils.getNewId()
  • timestamp - calls Utils.timestamp()
  • formatdate - calls Utils.formatDate(format, locale), additional parameters: format (e.g. “dd MM yyyy”), locale.
  • formatmessage - calls Utils.formatMessage(msg, params), additional parameters: message (the message to format)
  • nospaces - calls Utils.noSpaces(str, repl), additional parameters: string (the string to process)
  • nosymbols - calls Utils.stripAndTrim(str), additional parameters: string (the string to process)
  • md2html - calls Utils.markdownToHtml(md), additional parameters: md (a Markdown string)
  • timeago - calls HumanTime.approximately(delta), additional parameters: delta (time difference between now and then)

Example: GET /v1/utils?method=nospaces&string=some string with spaces

Request

  • {method} - the name of the method to call (one of the above)

Response

Returns a JSON object.

  • status codes - 200, 400 (if no method is specified)

Example response - returns the result without envelope:

"result"