Data Storage REST API
You can access the Data Storage service from any device that can send HTTP requests. There are many things you can do with our REST API. For example:
- You can manipulate data on the cloud with any programming language.
- If you want to migrate from TDS to other services, you can export all your data.
- Your mobile site can fetch data from the cloud with vanilla JavaScript if you regard importing the JavaScript SDK as an overkill.
- You can import new data in batches to be consumed by your app later.
- You can export recent data for offline analysis or additional incremental backup.
API Version
The current API version is 1.1
.
Testing
To help you easily test out our REST API, this page provides curl command examples. Those examples are targeted for users of unix-like platforms (including macOS and Linux), so you may need to modify the commands if you are using cmd.exe on Windows.
For example, \
in curl examples means to be continued on the next line, but cmd.exe will consider it as a path separator.
To make things easier, we recommend you to use Postman for testing on Windows.
You can directly import the curl commands shown on this page into Postman.
With Postman, you can also generate code for the languages and libraries of your choice for accessing our REST API.
Base URL
The Base URL for the REST API (represented with {{host}}
in curl examples) is the API domain of your app. You can manage and view it on the dashboard.
Objects
URL | HTTP Method | Functionality |
---|---|---|
/1.1/classes/<className> | POST | Create an object |
/1.1/classes/<className>/<objectId> | GET | Retrieve an object |
/1.1/classes/<className>/<objectId> | PUT | Update an object |
/1.1/classes/<className> | GET | Query objects |
/1.1/classes/<className>/<objectId> | DELETE | Delete an object |
/1.1/scan/classes/<className> | GET | Iterate over objects |
Users
URL | HTTP Method | Functionality |
---|---|---|
/1.1/users | POST | Register Connect user |
/1.1/usersByMobilePhone | POST | Register or log in via mobile phone |
/1.1/login | POST | Log in |
/1.1/users/<objectId> | GET | Retrieve a user |
/1.1/users/me | GET | Retrieve a user with session token |
/1.1/users/<objectId>/refreshSessionToken | PUT | Reset session token |
/1.1/users/<objectId>/updatePassword | PUT | Reset password with old password |
/1.1/users/<objectId> | PUT | Update user info Connect user Verify email |
/1.1/users | GET | Query users |
/1.1/users/<objectId> | DELETE | Delete a user |
/1.1/requestPasswordReset | POST | Request to reset password with email |
/1.1/requestEmailVerify | POST | Request to verify email |
Roles
URL | HTTP Method | Functionality |
---|---|---|
/1.1/roles | POST | Create a role |
/1.1/roles/<objectId> | GET | Retrieve a role |
/1.1/roles/<objectId> | PUT | Update a role |
/1.1/roles | GET | Query roles |
/1.1/roles/<objectId> | DELETE | Delete a role |
Data Schema
URL | HTTP Method | Functionality |
---|---|---|
/1.1/schemas | GET | Retrieve schemas of all classes |
/1.1/schemas/<className> | GET | Retrieve the schema of a class |
Others
URL | HTTP Method | Functionality |
---|---|---|
/1.1/date | GET | Retrieve server date and time |
/1.1/exportData | POST | Request to export all data from the app |
/1.1/exportData/<id> | GET | Retrieve the status and result of a data export job |
Request Format
For POST and PUT requests, the request body must be in JSON, and the Content-Type
HTTP header should be application/json
accordingly.
The X-LC-Id
and X-LC-Key
headers are used for authentication:
curl -X PUT \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{"content": "The content of this blog post"}' \
https://{{host}}/1.1/classes/Post/<objectId>
X-LC-Id
is the App ID
and X-LC-Key
is the App Key
or Master Key
.
A ,master
postfix is used to indicate that the value of X-LC-Key
is a Master Key
. For example:
X-LC-Key: {{masterkey}},master
Cross-origin resource sharing is supported so that you can use these headers with XMLHttpRequest
in JavaScript.
You can also use the Accept-Encoding
header to enable compression with gzip
or brotli
.
X-LC-Sign
You may also authenticate requests with X-LC-Sign
instead of X-LC-Key
to minimize the risk of leaking the App Key
:
curl -X PUT \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Sign: d5bcbb897e19b2f6633c716dfdfaf9be,1453014943466" \
-H "Content-Type: application/json" \
-d '{"content": "Updating a post with the X-LC-Sign header"}' \
https://{{host}}/1.1/classes/Post/<objectId>
The value of X-LC-Sign
is a string in the form of sign,timestamp[,master]
:
Name | Optionality | Description |
---|---|---|
sign | Required | Concat timestamp and App Key (or Master Key ), then calculate its MD5 hash value. |
timestamp | Required | The unix timestamp of the current request, accurate to milliseconds. |
master | Optional | Use this postfix to indicate that the Master Key is used. |
Please make sure the letters in the MD5 hash value in the sign
portion are in lowercase.
For example, given the following application:
App Id | FFnN2hso42Wego3pWq4X5qlu |
App Key | UtOCzqb67d3sN12Kts4URwy8 |
Master Key | DyJegPlemooo4X1tg94gQkw1 |
Request time | 2016-01-17 15:15:43.466 GMT+08:00 |
timestamp | 1453014943466 |
To calculate the sign with App Key
:
md5( timestamp + App Key )
= md5(1453014943466UtOCzqb67d3sN12Kts4URwy8)
= d5bcbb897e19b2f6633c716dfdfaf9be
-H "X-LC-Sign: d5bcbb897e19b2f6633c716dfdfaf9be,1453014943466" \
To calculate the sign with Master Key
:
md5( timestamp + Master Key )
= md5(1453014943466DyJegPlemooo4X1tg94gQkw1)
= e074720658078c898aa0d4b1b82bdf4b
-H "X-LC-Sign: e074720658078c898aa0d4b1b82bdf4b,1453014943466,master" \
Here ,master
is added to the end of the string to tell the server that the signature is generated with the Master Key.
Using master key will bypass all permission validations. Make sure you do not leak the master key and only use it in restrained environments.
Specifying Hook Invocation Environment
For requests that may trigger hooks on Cloud Engine, use the X-LC-Prod
HTTP header to specify the invocation environment:
X-LC-Prod: 0
means to use the staging environmentX-LC-Prod: 1
means to use the production environment
If you do not specify the X-LC-Prod
HTTP header, the hook in the production environment will be invoked.
Response Format
For all the requests made to the REST API, The response body is always a JSON object.
An HTTP status code is used to indicate whether a request succeeded or failed.
A 2xx status code indicates success, and a 4xx/5xx status code indicates the occurrence of an error.
When an error occurs, the response body will be a JSON object with two fields, code
and error
,
where code
is the error code (integer) and error
is a brief error message (string).
The code
may be identical to the HTTP status code,
but oftentimes it is a customized error code more specific than the HTTP status code.
For example, if you try to save an object with an invalid key name, you will see:
{
"code": 105,
"error": "Invalid key name. Keys are case-sensitive and 'a-zA-Z0-9_' are the only valid characters. The column is: 'invalid?'."
}
Objects
Object Format
The Data Storage service is built around objects. Each object consists of several key-value pairs, where values are in JSON-compatible formats. Objects are schemaless, so you do not need to allocate keys in advance. You only need to set key-value pairs as you wish and when needed.
For example, if you are implementing a Twitter-like social app, you may give the following attributes (key-value pairs) to a post:
{
"content": "Discover Superb Games.",
"pubUser": "TapTap",
"pubTimestamp": 1435541999
}
Keys can only contain letters, numbers, and underscores. Values can be anything encoded in JSON.
Each object belongs to a class (table in traditional database terms).
We recommend using CapitalizedWords
to name your classes, and mixedCases
to name your attributes.
This naming style helps to improve the readability of your code.
Each time when an object is saved to the cloud, a unique objectId
will be assigned to it.
createdAt
and updatedAt
will also be filled in by the cloud, which indicate the time the object is created and updated.
These attributes are reserved and you cannot modify them yourself.
For example, the object above could look like this when retrieved:
{
"content": "Discover Superb Games.",
"pubUser": "TapTap",
"pubTimestamp": 1435541999,
"createdAt": "2015-06-29T01:39:35.931Z",
"updatedAt": "2015-06-29T01:39:35.931Z",
"objectId": "558e20cbe4b060308e3eb36c"
}
createdAt
and updatedAt
are strings whose content is a UTC timestamp in ISO 8601 format with millisecond precision: YYYY-MM-DDTHH:MM:SS.MMMZ
.
objectId
is a string unique in the class, like the primary key of a relational database.
In our REST API, class-level operations use the class name as its endpoint.
For example, the URL for operations on a class named Post
will be:
https://{{host}}/1.1/classes/Post
There is also a special URL for users:
https://{{host}}/1.1/users
Object-specific operations use nested URLs under the class.
For example, the URL for operations on an object in the Post
class with 558e20cbe4b060308e3eb36c
as its objectId
will be:
https://{{host}}/1.1/classes/Post/558e20cbe4b060308e3eb36c
Creating Objects
To create a new object, send a POST request containing the object itself to the URL for the class. For example, to create the object we mentioned earlier:
curl -X POST \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{"content": "Discover Superb Games.","pubUser": "TapTap","pubTimestamp": 1435541999}' \
https://{{host}}/1.1/classes/Post
If succeeded, you will receive 201 Created
with a Location
header point to the URL of the object just created:
Status: 201 Created
Location: https://{{host}}/1.1/classes/Post/558e20cbe4b060308e3eb36c
And the response body is a JSON object with objectId
and createdAt
key-value pairs:
{
"createdAt": "2015-06-29T01:39:35.931Z",
"objectId": "558e20cbe4b060308e3eb36c"
}
To tell the cloud to return the full data of the new object, set the fetchWhenSave
parameter to true
:
curl -X POST \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{"content": "Discover Superb Games.","pubUser": "TapTap","pubTimestamp": 1435541999}' \
https://{{host}}/1.1/classes/Post?fetchWhenSave=true
Class names can only contain letters, numbers, and underscores. Every application can contain up to 500 classes, and each class can contain up to 300 fields. There is no limit on the number of objects in each class.
Retrieving Objects
After you create an object, you can send a GET request to the Location
of the response to fetch the object. For example, to fetch the object we just created:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
https://{{host}}/1.1/classes/Post/558e20cbe4b060308e3eb36c
The response body is a JSON object containing all the attributes you gave to the object, as well as the three preserved attributes (objectId
, createdAt
, and updatedAt
):
{
"content": "Discover Superb Games.",
"pubUser": "TapTap",
"pubTimestamp": 1435541999,
"createdAt": "2015-06-29T01:39:35.931Z",
"updatedAt": "2015-06-29T01:39:35.931Z",
"objectId": "558e20cbe4b060308e3eb36c"
}
If the object contains pointers to other objects, you can add an include
parameter to indicate that you wish to retrieve these objects as well. For example, if a post has an author
field indicating the person who posted it, you can retrieve the post together with its author in this way:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'include=author' \
https://{{host}}/1.1/classes/Post/<objectId>
You can use a dot (.
) in the value of the include
parameter to further include the object pointed by a pointed object. For example, assuming that there is a department
field for each author, you can use include=author.department
to retrieve the information of the department.
If the class does not exist, you will receive a 404 Not Found
error:
{
"code": 101,
"error": "Class or object doesn't exists."
}
If the server cannot find the object according to the objectId
you specified, you will receive an empty object (with HTTP status code being 200 OK
):
{}
One exception is that for the built-in classes (those with a name starting with a leading underscore), you may get a different result when trying to retrieve an object with an invalid objectId
.
For example, when retrieving a _User
object with an objectId
that does not exist, you will get a 400 Bad Request
error.
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
https://{{host}}/1.1/classes/_User/<NonexistObjectId>
The request above will lead to the following response:
{
"code": 211,
"error": "Could not find user."
}
We recommend using GET /users/<objectId>
to fetch user information instead of directly querying the _User
class.
See also Retrieving Users.
Updating Objects
To update an object, you can send a PUT request to the object URL.
The server will only update the attributes you explicitly specified in the request (except for updatedAt
).
For example, to only update the content
of a post:
curl -X PUT \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{"content": "Discover Superb Games. https://www.taptap.io/"}' \
https://{{host}}/1.1/classes/Post/<objectId>
If the update succeeds, a JSON object containing an updatedAt
property will be returned:
{
"updatedAt": "2015-06-30T18:02:52.248Z"
}
The fetchWhenSave
parameter can also be used when updating an object.
Keep in mind that you will only get the updated fields instead of all the fields of the object.
Counter
For an existing post in our app, we may want to keep track of how many people liked it. However, since a lot of likes could happen at the same time, if we have the client retrieve the value of the number of likes, update it, and store the new value back to the cloud, there will likely be conflicts that cause the number stored on the cloud to be inaccurate.
To solve the problem, you can use the Increment
atomic operator to increase a counter-like attribute:
curl -X PUT \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{"upvotes":{"__op":"Increment","amount":1}}' \
https://{{host}}/1.1/classes/Post/<objectId>
The command above adds 1 to the upvotes
property of the object. You can specify how much you want to increment with the amount
parameter. The number can be negative to indicate a subtraction from the original value.
There is also a Decrement
operator.
Decrement
ing a positive number is equivalent to Increment
ing a negative number.
Bitwise Operators
There are three bitwise operators for integers:
BitAnd
: Bitwise ANDBitOr
: Bitwise ORBitXor
: Bitwise XOR
curl -X PUT \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{"flags":{"__op":"BitOr","value": 0x0000000000000004}}' \
https://{{host}}/1.1/classes/Post/<objectId>
Arrays
There are three atomic operators for arrays:
Add
extends an array attribute by appending elements to the given array.AddUnique
is similar toAdd
, but only appends elements not already contained in the array attribute.Remove
removes all occurrences of elements specified in the given array.
The given array mentioned above is passed in as the value of the objects
key.
For example, to add some tags to the post:
curl -X PUT \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{"tags":{"__op":"AddUnique","objects":["Frontend","JavaScript"]}}' \
https://{{host}}/1.1/classes/Post/<objectId>
Conditional Updates
Suppose we are going to deduct some money from an Account
, and we want to make sure this deduction will not result in a negative balance.
We can use conditional updates by adding a where
parameter with the condition balance >= amount
:
curl -X PUT \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{"balance":{"__op":"Decrement","amount": 30}}' \
"https://{{host}}/1.1/classes/Account/558e20cbe4b060308e3eb36c?where=%7B%22balance%22%3A%7B%22%24gte%22%3A%2030%7D%7D"
Here %7B%22balance%22%3A%7B%22%24gte%22%3A%2030%7D%7D
is the URL-encoded condition {"balance":{"$gte": 30}}
.
If the condition is not met, the update will not be performed, and you will receive a 305
error:
{
"code": 305,
"error": "No effect on updating/deleting a document."
}
Note: where
must be passed in as a query parameter of the URL.
A List of __op Operations
You can perform atomic operations with the __op("Method", {JSON parameters})
function.
Operation | Description | Example |
---|---|---|
Delete | Delete a property of the object | __op('Delete', {'delete': true}) |
Add | Add objects to the end of an array | __op('Add',{'objects':['Apple','Google']}) |
AddUnique | Add each of the objects to the end of an array only if the object does not exist in the array | __op('AddUnique', {'objects':['Apple','Google']}) |
Remove | Delete objects from the array | __op('Remove',{'objects':['Apple','Google']}) |
AddRelation | Add a relation | __op('AddRelation', {'objects':[pointer('_User','558e20cbe4b060308e3eb36c')]}) |
RemoveRelation | Remove a relation | __op('RemoveRelation', {'objects':[pointer('_User','558e20cbe4b060308e3eb36c')]}) |
Increment | Increment | __op('Increment', {'amount': 50}) |
Decrement | Decrement | __op('Decrement', {'amount': 50}) |
BitAnd | Bitwise AND | __op('BitAnd', {'value': 0x0000000000000004}) |
BitOr | Bitwise OR | __op('BitOr', {'value': 0x0000000000000004}) |
BitXor | Bitwise XOR | __op('BitXor', {'value': 0x0000000000000004}) |
Deleting Objects
To delete an object, send a DELETE
request to the URL for the object:
curl -X DELETE \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
https://{{host}}/1.1/classes/Post/<objectId>
To delete an attribute from an object, send a PUT
request with the Delete
operator:
curl -X PUT \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{"downvotes":{"__op":"Delete"}}' \
https://{{host}}/1.1/classes/Post/<objectId>
Conditional Deletions
Similar to conditional updates, we pass a URL-encoded where
parameter to the DELETE
request to conditionally delete the object. For example, to delete a post if its clicks
equals to 0
:
curl -X DELETE \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
"https://{{host}}/1.1/classes/Post/<objectId>?where=%7B%22clicks%22%3A%200%7D"
Here %7B%22clicks%22%3A%200%7D
is the URL-encoded value for {"clicks": 0}
.
Again, if the condition is not met, the update will not be performed, and you will receive a 305
error:
{
"code": 305,
"error": "No effect on updating/deleting a document."
}
Keep in mind that where
must be a query parameter in the URL.
Iterating Over Objects
For classes with a moderate number of objects, we can iterate over all the objects in the class by constructing queries with skip
, limit
, and order
.
However, for classes with a large number of objects, there will be a performance issue if we keep using skip
.
To avoid this problem, we can do pagination by specifying the scope of createdAt
or updatedAt
.
There is a scan
endpoint that is dedicated for this purpose: it can be used to iterate over the objects in a class ordered by a given field. Using scan
makes things easier compared to constructing queries manually by specifying the scopes of createdAt
or updatedAt
.
By default, scan
returns 100 results in ascending order by objectId
. You can ask the cloud to return up to 1000 results by specifying the limit
parameter:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{masterkey}},master" \
-G \
--data-urlencode 'limit=10' \
https://{{host}}/1.1/scan/classes/Article
Be sure to use the MasterKey
when using scan
.
The cloud will return a results
array and a cursor
.
{
"results": [
{
"tags": ["clojure", "\u7b97\u6cd5"],
"createdAt": "2016-07-07T08:54:13.250Z",
"updatedAt": "2016-07-07T08:54:50.268Z",
"title": "clojure persistent vector",
"objectId": "577e18b50a2b580057469a5e"
}
//...
],
"cursor": "pQRhIrac3AEpLzCA"
}
The cursor
will be null
if there are no more results.
If cursor
is not null
, you can use scan
again with the value of cursor
to continue the iteration:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{masterkey}},master" \
-G \
--data-urlencode 'limit=10' \
--data-urlencode 'cursor=pQRhIrac3AEpLzCA' \
https://{{host}}/1.1/scan/classes/Article
Each cursor
must be consumed within 10 minutes.
It becomes invalid after 10 minutes.
You can also specify where
conditions for filtering:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{masterkey}},master" \
-G \
--data-urlencode 'limit=10' \
--data-urlencode 'where={"score": 100}' \
https://{{host}}/1.1/scan/classes/Article
As mentioned above, by default the results are in ascending order by objectId
.
To return results ordered by another attribute,
pass that attribute as the scan_key
parameter:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{masterkey}},master" \
-G \
--data-urlencode 'limit=10' \
--data-urlencode 'scan_key=score' \
https://{{host}}/1.1/scan/classes/Article
To return results in descending order, prefix a minus sign (-
) to the value of the scan_key
, e.g., -score
.
The value of the scan_key
passed must be strictly monotonous, and it cannot be used in where
conditions.
You cannot set the include
parameter when using scan
.
If you wish to use include
when iterating over the objects in a class, please use a basic query with setting the scopes of createdAt
and updatedAt
instead.
Batch Operations
To reduce the number of requests you make, you can wrap create, update, and delete operations on multiple objects in one request.
You can assign each operation its own method, path, and body, which replaces the HTTP requests you would ordinarily make. The operations will be executed according to the order they are sent to the server. For example, to make a series of posts at once:
curl -X POST \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{
"requests": [
{
"method": "POST",
"path": "/1.1/classes/Post",
"body": {
"content": "Post 1",
"pubUser": "TapTap"
}
},
{
"method": "POST",
"path": "/1.1/classes/Post",
"body": {
"content": "Post 2",
"pubUser": "TapTap"
}
}
]
}' \
https://{{host}}/1.1/batch
Currently, there is no limit on the number of operations in each request, but there is a 20 MB size limit on the request body for all API requests. It is recommended that you load at most 100 operations into each request.
The response body will be an array with the length and order of the members corresponding to those of the operations in the request.
Each member will be a JSON object with one and only one key, and that key will be either success
or error
.
The value of success
or error
will be the response to the corresponding single request on success or failure respectively.
[
{
"error": {
"code": 1,
"error": "Could not find object by id '558e20cbe4b060308e3eb36c' for class 'Post'."
}
},
{
"success": {
"updatedAt": "2017-02-22T06:35:29.419Z",
"objectId": "58ad2e850ce463006b217888"
}
}
]
Be aware that the HTTP status 200
returned by a batch request only means the cloud had received and performed the operations.
It does not mean that all the operations within the batch request succeeded.
Besides the POST
requests in the above example,
you can also wrap PUT
and DELETE
requests in a batch request:
curl -X POST \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{
"requests": [
{
"method": "PUT",
"path": "/1.1/classes/Post/55a39634e4b0ed48f0c1845b",
"body": {
"upvotes": 2
}
},
{
"method": "DELETE",
"path": "/1.1/classes/Post/55a39634e4b0ed48f0c1845c"
}
]
}' \
https://{{host}}/1.1/batch
Batch requests can also be used to replace requests with very long URLs (usually constructed with very complex queries or conditions) to bypass the limit on URL length enforced by the server side or the client side.
Advanced Data Types
Besides standard JSON values, we also support advanced data types like Date, Byte, and Pointer.
These advanced data types are encoded as a JSON object with a __type
key.
Date contains an iso
key, whose value is a UTC timestamp string in ISO 8601 format with millisecond precision: YYYY-MM-DDTHH:MM:SS.MMMZ
.
{
"__type": "Date",
"iso": "2015-06-21T18:02:52.249Z"
}
The Date type can be useful when you perform queries on the built-in createdAt
and updatedAt
fields. For example, to query all the posts made on a specific time:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-G \
--data-urlencode 'where={"createdAt":{"$gte":{"__type":"Date","iso":"2015-06-21T18:02:52.249Z"}}}' \
https://{{host}}/1.1/classes/Post
Keep in mind that since createdAt
and updatedAt
are built-in fields, when their values are appearing in the response of a request, they will be in UTC timestamps rather than encoded Dates.
Byte contains a base64
key, whose value is a MIME base64 string (with no whitespace characters).
{
"__type": "Bytes",
"base64": "5b6I5aSa55So5oi36KGo56S65b6I5Zac5qyi5oiR5Lus55qE5paH5qGj6aOO5qC877yM5oiR5Lus5bey5bCGIExlYW5DbG91ZCDmiYDmnInmlofmoaPnmoQgTWFya2Rvd24g5qC85byP55qE5rqQ56CB5byA5pS+5Ye65p2l44CC"
}
Pointer contains a className
key and an objectId
key, whose values are the corresponding class name and objectId of the pointed value.
{
"__type": "Pointer",
"className": "Post",
"objectId": "55a39634e4b0ed48f0c1845c"
}
A pointer to a user contains a className
of _User
.
The leading underscore indicates that _User
is a built-in class.
Similarly, pointers to roles and installations contain a className
of _Role
or _Installation
respectively.
However, a pointer to a file is a special case:
{
"id": "543cbaede4b07db196f50f3c",
"__type": "File"
}
GeoPoint contains latitude
and longitude
of the location:
{
"__type": "GeoPoint",
"latitude": 39.9,
"longitude": 116.4
}
We may add more advanced data types in the future, so you should not use __type
as the key of your own JSON objects.
Queries
Basic Queries
To list objects in a class, just send a GET request to the class URL. For example, to get all the posts:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
https://{{host}}/1.1/classes/Post
The response body is a JSON object containing a results
key,
whose value is an array of objects:
{
"results": [
{
"content": "Post 1",
"pubUser": "TapTap",
"upvotes": 2,
"createdAt": "2015-06-29T03:43:35.931Z",
"objectId": "55a39634e4b0ed48f0c1845b"
},
{
"content": "Post 2",
"pubUser": "TapTap",
"pubTimestamp": 1435541999,
"createdAt": "2015-06-29T01:39:35.931Z",
"updatedAt": "2015-06-29T01:39:35.931Z",
"objectId": "558e20cbe4b060308e3eb36c"
}
]
}
The values of createdAt
and updatedAt
you see on the dashboard are in the timezone of your local computer, and it is the same for the values obtained through our SDKs. However, when using the REST API, you will get those values in UTC. You can convert them to local times yourself if you need them.
Query Constraints
The where
parameter can be used to apply query constraints.
It should be encoded as JSON first, then URL encoded.
The simplest form of where
parameter is a key-value pair (exact match).
For example, to query posts published by TapTap
:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-G \
--data-urlencode 'where={"pubUser":"TapTap"}' \
https://{{host}}/1.1/classes/Post
Other operators available for the where
parameter:
Operator | Description |
---|---|
$ne | Not equal to |
$lt | Less than |
$lte | Less than or equal to |
$gt | Greater than |
$gte | Greater than or equal to |
$regex | Match a regular expression |
$in | Contain |
$nin | Not contain |
$all | Contain all (for array type) |
$exists | The given key exists |
$select | Match the result of another query |
$dontSelect | Not match the result of another query |
For example, to query all the posts published on 2015-06-29:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-G \
--data-urlencode 'where={"createdAt":{"$gte":{"__type":"Date","iso":"2015-06-29T00:00:00.000Z"},"$lt":{"__type":"Date","iso":"2015-06-30T00:00:00.000Z"}}}' \
https://{{host}}/1.1/classes/Post
To query all the posts whose number of votes is an odd number less than 10:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-G \
--data-urlencode 'where={"upvotes":{"$in":[1,3,5,7,9]}}' \
https://{{host}}/1.1/classes/Post
To query all the posts not published by TapTap:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-G \
--data-urlencode 'where={"pubUser":{"$nin":["TapTap"]}}' \
https://{{host}}/1.1/classes/Post
To query all the posts that have been voted by someone:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-G \
--data-urlencode 'where={"upvotes":{"$exists":true}}' \
https://{{host}}/1.1/classes/Post
To query all the posts that have not been voted:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-G \
--data-urlencode 'where={"upvotes":{"$exists":false}}' \
https://{{host}}/1.1/classes/Post
Suppose we use _Followee
and _Follower
classes for the following relationship, then we can query posts published by someone followed by the current user like this:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-G \
--data-urlencode 'where={
"author": {
"$select": {
"query": {
"className":"_Followee",
"where": {
"user":{
"__type": "Pointer",
"className": "_User",
"objectId": "55a39634e4b0ed48f0c1845c"
}
}
},
"key":"followee"
}
}
}' \
https://{{host}}/1.1/classes/Post
The order
parameter can be used to specify how the returned objects shuold be sorted. For example, to query posts and sort them in ascending order by creation time:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'order=createdAt' \
https://{{host}}/1.1/classes/Post
To sort them in descending order:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'order=-createdAt' \
https://{{host}}/1.1/classes/Post
To sort results by multiple keys, use a comma to separate the keys. For example, to sort posts in ascending order by createdAt
and descending order by pubUser
:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'order=createdAt,-pubUser' \
https://{{host}}/1.1/classes/Post
You can implement paginiation with limit
and skip
. limit
defaults to 100 but you can set it to any integer between 1 and 1000. If you give it a value out of this range, the default value (100) will be used. For example, to get the first 200 posts after the 400th:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'limit=200' \
--data-urlencode 'skip=400' \
https://{{host}}/1.1/classes/Post
You can limit the fields being returned from the server by providing a keys
parameter in your request. For example, to only include pubUser
and content
in the response (together with the built-in fields objectId
, createdAt
, and updatedAt
):
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'keys=pubUser,content' \
https://{{host}}/1.1/classes/Post
You can further limit the returned values to the properties of a given field. For example, to only get the family names of the authors, you can write keys=pubUser.familyName
.
You can also specify which fields you want to exclude from the response by adding a minus sign before the field names. For example, to exclude the author
field:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'keys=-author' \
https://{{host}}/1.1/classes/Post
The inverted selection applies to preserved attributes as well. For example, you can write keys=-createdAt,-updatedAt,-objectId
.
You can also use it with dot notation, e.g., keys=-pubUser.createdAt,-pubUser.updatedAt
.
Including ACL in the Response
By default, the response will not contain the ACL field.
The ACL field will only be included if you enabled Include ACL with objects being queried under Developer Center > Your Game > Game Services > Cloud Services > Data Storage > Settings > Queries and you included returnACL=true
in the request.
All the parameters mentioned above can be combined.
Regex Queries
To query posts whose title begins with WTO
:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-G \
--data-urlencode 'where={"title":{"$regex":"^WTO.*","$options":"i"}}' \
https://{{host}}/1.1/classes/Post
We will use the following dataset to demonstrate how you can use $options
to match different values of title:
{ "_id" : 100, "title" : "Single line description." },
{ "_id" : 101, "title" : "First line\nSecond line" },
{ "_id" : 102, "title" : "Many spaces before line" },
{ "_id" : 103, "title" : "Multiple\nline description" },
{ "_id" : 104, "title" : "abc123" }
Option | Description | Example |
---|---|---|
i | Case-insensitive search | {"$regex":"single", "$options":"i"} will match
|
m | Multi-line search Can be used for strings containing \n | {"$regex":"^S", "$options":"m"} (starts with capital “S”) will match
|
x | Free-spacing and line comments Includes spaces, tabs, \n , and comments starting with # ,but does not include vertical tabs (ASCII: 11). | {"$regex":"abc #category code\n123 #item number", "$options":"x"} (comments after #) will match
|
s | Allow . to match newline characters | {"$regex":"m.*line", "$options":"si"} will match
|
The options above can be combined, for example "$options":"sixm"
.
Array Queries
With key
being an array field, to query all the objects with key
containing 2
:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'where={"arrayKey":2}' \
https://{{host}}/1.1/classes/TestObject
To query all the objects with key
containing 2
, 3
, or 4
:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'where={"arrayKey":{"$in":[2,3,4]}}' \
https://{{host}}/1.1/classes/TestObject
Using $all
to query all the objects with key
containing 2
, 3
, and 4
:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'where={"arrayKey":{"$all":[2,3,4]}}' \
https://{{host}}/1.1/classes/TestObject
Using $size
to query all the objects with key
containing exactly 3 objects:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'where={"arrayKey":{"$size": 3}}' \
https://{{host}}/1.1/classes/TestObject
Pointer Queries
There are a couple of ways you can use to query relations between objects. If you want to query objects that have a field pointing to a specific object, you can construct a Pointer and pass it to the where
parameter. Assuming there is a Comment
class with a post
field pointing to the Post
class, you can query all the comments under a post with this command:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'where={"post":{"__type":"Pointer","className":"Post","objectId":"558e20cbe4b060308e3eb36c"}}' \
https://{{host}}/1.1/classes/Comment
To query objects with pointers according to another query on the pointed object, you can use the $inQuery
operator. Keep in mind that the default value of limit
is 100 and the maximum value of it is 1000, and this restriction applies to inner queries as well. You may need to carefully construct queries to get your expected result.
For example, assuming each post has an image
field, to query all the comments on posts with attached images:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'where={"post":{"$inQuery":{"where":{"image":{"$exists":true}},"className":"Post"}}}' \
https://{{host}}/1.1/classes/Comment
To include pointed objects in one query, use the include
parameter.
For example, to query the most recent 10 comments with the posts commented on:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'order=-createdAt' \
--data-urlencode 'limit=10' \
--data-urlencode 'include=post' \
https://{{host}}/1.1/classes/Comment
Without the include
parameter, the post attribute of the returned comments will look like this:
{
"__type": "Pointer",
"className": "Post",
"objectId": "51e3a359e4b015ead4d95ddc"
}
With the include=post
parameter, the post attribute will be dereferenced:
{
"__type": "Object",
"className": "Post",
"objectId": "51e3a359e4b015ead4d95ddc",
"createdAt": "2015-06-29T09:31:20.371Z",
"updatedAt": "2015-06-29T09:31:20.371Z",
"desc": "this is a post"
}
You can use dots (.
) for multi-level dereference. For example, to get the author
s of the posts pointed by comments:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'order=-createdAt' \
--data-urlencode 'limit=10' \
--data-urlencode 'include=post.author' \
https://{{host}}/1.1/classes/Comment
And you can use comma (,
) to separate multiple pointers to include
.
GeoPoint Queries
Early on we have briefly described GeoPoint.
Assuming we are including the location information of each post in the location
field, you can use the $nearSphere
operator to query nearby objects. For example, to retrieve 10 posts whose locations are the closest to the current location:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'limit=10' \
--data-urlencode 'where={
"location": {
"$nearSphere": {
"__type": "GeoPoint",
"latitude": 39.9,
"longitude": 116.4
}
}
}' \
https://{{host}}/1.1/classes/Post
The returned results will be ordered by distance, with the first result being the post published at the nearest location.
This order can be overridden by the order
parameter.
To limit the maximum distance, you can use $maxDistanceInMiles
, $maxDistanceInKilometers
, or $maxDistanceInRadians
. For example, to limit the distance to 10 miles:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'where={
"location": {
"$nearSphere": {
"__type": "GeoPoint",
"latitude": 39.9,
"longitude": 116.4
},
"$maxDistanceInMiles": 10.0
}
}' \
https://{{host}}/1.1/classes/Post
You can also query for objects within a rectangular area with this format: {"$within": {"$box": [southwestGeoPoint, northeastGeoPoint]}}
.
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'where={
"location": {
"$within": {
"$box": [
{
"__type": "GeoPoint",
"latitude": 39.97,
"longitude": 116.33
},
{
"__type": "GeoPoint",
"latitude": 39.99,
"longitude": 116.37
}
]
}
}
}' \
https://{{host}}/1.1/classes/Post
Be aware that the range of latitude
is [-90.0, 90.0]
, and the range of longitude
is [-180.0, 180.0]
.
There is currently one limit on GeoPoints: every class can only contain one GeoPoint attribute.
File Queries
Querying files is similar to querying normal objects. For example, to query all files (just like querying normal objects, it returns at most 100 results by default):
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
https://{{host}}/1.1/classes/files
Be aware that the url
s of the internal files (those uploaded to the cloud) are automatically generated by the cloud and are applied with the logic related to updating custom domains.
This means that you should only query external files (those saved with URLs) with the url
field. For internal files, please query with the key
field (the path in the URL) instead.
For the same reason, when iterating over files with scan
, the internal files in the result will not have the url
field but only the key
field.
Counting Results
You can pass count=1
parameter to retrieve the count of matched results.
For example, if you just need to know how many posts a specific user has made:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'where={"pubUser":"TapTap"}' \
--data-urlencode 'count=1' \
--data-urlencode 'limit=0' \
https://{{host}}/1.1/classes/Post
Since limit=0
, only the count
will be returned, and the results
array will be empty.
{
"results": [],
"count": 7
}
Given a nonzero limit
parameter, results will be returned together with the count.
Compound Queries
You can use the $or
operator to query objects matching any one of the several queries. For example, to query posts made by official accounts and personal accounts:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'where={"$or":[{"pubUserCertificate":{"$gt":2}},{"pubUserCertificate":{"$lt":3}}]}' \
https://{{host}}/1.1/classes/Post
Similarly, you can use $and
operator to query objects matching all subqueries. For example, to query all the objects that have the price
field and with price
not equaling to 199
:
where={"$and":[{"price": {"$ne":199}},{"price":{"$exists":true}}]}
The query condition expressions are implicitly combined with the $and
operator, so the query expression above could also be rewritten as:
where=[{"price": {"$ne":199}},{"price":{"$exists":true}}]
In fact, since both conditions are targeted at the same field (price
), the above query expression can be further simplified to:
where={"price": {"$ne":199, "$exists":true}}
However, to combine two or more OR-ed queries, you have to use the $and
operator:
where={"$and":[{"$or":[{"pubUserCertificate":{"$gt":2}},{"pubUserCertificate":{"$lt":3}}]},{"$or":[{"pubUser":"TapTap"},{"pubUser":"TDS"}]}]}
Be aware that non-filtering constraints such as limit
, skip
, order
, and include
are not allowed in subqueries of a compound query.
Users
With the users API, you can build an account system for your application quickly and conveniently.
Users (the _User
class) share many traits with other classes. For example, _User
is schema-free as well.
However, all user objects must have username
and password
attributes. password
will be encrypted automatically.
username
and email
(if available) attributes must be unique (case sensitive).
Signing Up
To create a new user:
curl -X POST \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{"username":"tom","password":"f32@ds*@&dsa","phone":"18612340000"}' \
https://{{host}}/1.1/users
As mentioned above, username
and password
are required. password
will be stored in encrypted form, and the cloud will never return its value to the client side.
If the registration succeeds, the cloud will return 201 Created
and the Location
will contain the URL for that user:
Status: 201 Created
Location: https://{{host}}/1.1/users/55a47496e4b05001a7732c5f
The response body is a JSON object containing three attributes:
{
"sessionToken": "qmdj8pdidnmyzp0c7yqil91oc",
"createdAt": "2015-07-14T02:31:50.100Z",
"objectId": "55a47496e4b05001a7732c5f"
}
Logging In
To log in with username and password:
curl -X POST \
-H "Content-Type: application/json" \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-d '{"username":"tom","password":"f32@ds*@&dsa"}' \
https://{{host}}/1.1/login
You can also log in with email and password by replacing the body with:
{ "email": "[email protected]", "password": "f32@ds*@&dsa" }
Or, log in with phone number and password:
{ "mobilePhoneNumber": "+86186xxxxxxxx", "password": "f32@ds*@&dsa" }
The response body is a JSON object containing all the attributes of that user, except password
:
{
"sessionToken": "qmdj8pdidnmyzp0c7yqil91oc",
"updatedAt": "2015-07-14T02:31:50.100Z",
"phone": "18612340000",
"objectId": "55a47496e4b05001a7732c5f",
"username": "tom",
"createdAt": "2015-07-14T02:31:50.100Z",
"emailVerified": false,
"mobilePhoneVerified": false
}
Refresh sessionToken
To refresh a user's sessionToken
:
curl -X PUT \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "X-LC-Session: qmdj8pdidnmyzp0c7yqil91oc" \
https://{{host}}/1.1/users/57e3bcca67f35600577c3063/refreshSessionToken
X-LC-Session
can be omitted when using Master Key.
If succeeded, a new sessionToken
will be returned, with user information:
{
"sessionToken": "5frlikqlwzx1nh3wzsdtfr4q7",
"updatedAt": "2016-10-20T03:10:57.926Z",
"objectId": "57e3bcca67f35600577c3063",
"username": "tom",
"createdAt": "2016-09-22T11:13:14.842Z",
"emailVerified": false,
"mobilePhoneVerified": false
}
Locking Users
Seven consecutive failed login attempts for a user within 15 minutes will trigger a lock. Once this happens, the cloud will return the following error:
{
"code": 219,
"error": "Tried too many times to signin."
}
The cloud will release this lock automatically in 15 minutes after the last login failure. You cannot adjust this behavior via SDK or REST API. During the locking period, the user is not allowed to log in, even if they provide the correct password. This restriction also applies to SDK and Cloud Engine.
Verifying Email Address
Once a user clicked the verification link in the email, their emailVerified
will be set to true
.
emailVerified
is a Boolean with 3 statuses:
true
: the user has verified their email address via clicking the link in the verification mail.false
: when a user'semail
attribute is set or modified, the cloud will set theiremailVerified
tofalse
and send a verification email to the user. After the user clicks the verification link in the email, the cloud will setemailVerified
totrue
.null
: The user does not have anemail
, or the user object is created when the verifying new user's email address option is disabled.
The verification link expires in one week. To resend the verification email:
curl -X POST \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]"}' \
https://{{host}}/1.1/requestEmailVerify
Resetting Password
A user can reset their password via email:
curl -X POST \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]"}' \
https://{{host}}/1.1/requestPasswordReset
If succeed, the response body will be an empty JSON object:
{}
Retrieving Users
To retrieve a user, you can send a GET request to the user URL (as in the Location
header returned on successful signing up).
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
https://{{host}}/1.1/users/55a47496e4b05001a7732c5f
Alternatively, you can retrieve a user via their sessionToken
:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "X-LC-Session: qmdj8pdidnmyzp0c7yqil91oc" \
https://{{host}}/1.1/users/me
The returned JSON object is the same as in /login
.
If the user does not exist, a 400 Bad Request
will be returned:
{
"code": 211,
"error": "Could not find user."
}
Updating Users
Similar to Updating Objects, you can send a PUT
request to the user URL to update a user's data.
curl -X PUT \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "X-LC-Session: qmdj8pdidnmyzp0c7yqil91oc" \
-H "Content-Type: application/json" \
-d '{"phone":"18600001234"}' \
https://{{host}}/1.1/users/55a47496e4b05001a7732c5f
The X-LC-Session
HTTP header is to authenticate the modification,
whose value is the user's sessionToken
.
If succeed, updatedAt
will be returned.
This is the same as Updating Objects.
If you want to update username
, then you have to ensure that the new value of username
must not conflict with other existing users.
If you want to update password
after verifying the old password,
you can use PUT /1.1/users/:objectId/updatePassword
instead.
curl -X PUT \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "X-LC-Session: qmdj8pdidnmyzp0c7yqil91oc" \
-H "Content-Type: application/json" \
-d '{"old_password":"the_old_password", "new_password":"the_new_password"}' \
https://{{host}}/1.1/users/55a47496e4b05001a7732c5f/updatePassword
Note that this API still requires the X-LC-Session
header.
Querying Users
You can query users like how you query regular objects by sending GET
requests to /1.1/users
.
However, for security concerns, all queries on users will be rejected by the cloud unless you use the master key or have properly configured the _User
class' ACL settings.
Deleting Users
Just like deleting an object, you can send a DELETE
request to delete a user.
curl -X DELETE \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "X-LC-Session: qmdj8pdidnmyzp0c7yqil91oc" \
https://{{host}}/1.1/users/55a47496e4b05001a7732c5f
The X-LC-Session
HTTP header is used for authenticating this request.
Linking Users
To allow users to use third-party accounts to log in to your application, you can use the authData
attribute of users.
authData
is a JSON object whose schema may be different for different services.
The simplest form of authData
is as follows:
{
"anonymous": {
"id": "random UUID with lowercase hexadecimal digits"
// other optional keys
}
}
This is used for anonymous users, for example, to provide a "try it before signing up" or "guest login" feature for your application.
The authData
for an arbitrary platform:
{
"platform_name": {
"uid": "unique user id on that platform (string)",
"access_token": "access token for the user"
// other optional keys
}
}
authData
can have other additional keys, but it must contain both uid
and access_token
.
The cloud will automatically create a unique index for authData.platform_name.uid
.
This avoids binding a third-party account to multiple users.
However, you need to verify authData
yourself (except for certain platforms, see below).
Example authData
objects:
{
"authData": {
"lc_apple": {
"uid": "user identifier",
"identity_token": "identity token",
"code": "authorization code"
}
}
}
{
"taptap": {
"kid": "mac_key id",
"access_token": "Same as kid",
"token_type": "mac",
"mac_key": "mac key",
"mac_algorithm": "hmac-sha-1",
"openid": "The unique identifier of the user; a user has different openid's for different apps",
"name": "username",
"avatar": "URL of the user's avatar",
"unionid": "The unique identifier of the user; a user has the same unionid for all the apps under the same developer"
}
}
Other platforms:
{
"platform name, like facebook": {
"uid": "A unique identifier of the user from the platform",
"access_token": "Access Token"
// ……optional properties
}
}
Third-Party Signing Up and Login
To sign up or log in via a third party account, you also send a POST request with the authData
.
For example, to log in with Apple:
curl -X POST \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{
"authData": {
"lc_apple": {
"uid": "user identifier",
"identity_token": "identity token",
"code": "authorization code"
}
}
}' \
https://{{host}}/1.1/users
The response body will be a JSON object whose content is similar to the one returned when creating or logging in as a regular user.
A new user will be automatically assigned with a random username, e.g., ec9m07bo32cko6soqtvn6bko5
.
Linking a Third-Party Account
To link a third-party account to an existing user,
just update this user's authData
attribute.
curl -X PUT \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "X-LC-Session: qmdj8pdidnmyzp0c7yqil91oc" \
-H "Content-Type: application/json" \
-d '{
"lc_apple": {
"uid": "user identifier",
"identity_token": "identity token",
"code": "authorization code"
}
}' \
https://{{host}}/1.1/users/55a47496e4b05001a7732c5f
This user can be authenticated via matching authData
afterward.
Unlinking a Third-Party Account
Similarly, to unlink a user from a third party account,
just delete the platform in their authData
attribute.
curl -X PUT \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "X-LC-Session: 6fehqhr2t2na5mv1aq2om7jgz" \
-H "Content-Type: application/json" \
-d '{"authData.lc_apple":{"__op":"Delete"}}' \
https://{{host}}/1.1/users/5b7e53a767f356005fb374f6
Roles
The Data Storage service has a preserved class _Role
for roles.
For security concerns, roles are typically created and managed manually or via a separate management interface, not directly in your app.
Creating Roles
Creating a role is similar to creating an object, except that you must specify the name
and ACL
attributes. To prevent allowing wrong users to modify a role accidentally, you should set a restrictive and rigid ACL
.
curl -X POST \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{
"name": "Manager",
"ACL": {
"*": {
"read": true
}
}
}' \
https://{{host}}/1.1/roles
The response is the same as creating an object.
To create a role with existing child roles and users:
curl -X POST \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{
"name": "CLevel",
"ACL": {
"*": {
"read": true
}
},
"roles": {
"__op": "AddRelation",
"objects": [
{
"__type": "Pointer",
"className": "_Role",
"objectId": "55a48351e4b05001a774a89f"
}
]
},
"users": {
"__op": "AddRelation",
"objects": [
{
"__type": "Pointer",
"className": "_User",
"objectId": "55a47496e4b05001a7732c5f"
}
]
}
}' \
https://{{host}}/1.1/roles
You may have noticed that there is a new operator AddRelation
we have not seen before.
This operator adds a Relation to an object.
The actual implementation of Relation is quite complicated for performance issues,
but conceptually you can consider a Relation as an array of pointers, and they are only used in roles.
Retrieving Roles
Retrieving a role is similar to retrieving an object:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
https://{{host}}/1.1/roles/55a483f0e4b05001a774b837
The response body will be a JSON object:
{
"name": "CLevel",
"createdAt": "2015-07-14T03:37:20.992Z",
"updatedAt": "2015-07-14T03:37:20.994Z",
"objectId": "55a483f0e4b05001a774b837",
"users": {
"__type": "Relation",
"className": "_User"
},
"roles": {
"__type": "Relation",
"className": "_Role"
}
}
Updating Roles
Updating roles are similar to updating objects, except that name
cannot be modified, as mentioned above.
To add or remove users and child roles, you can use AddRelation
and RemoveRelation
operators.
Suppose we have a Manager
role with objectId 55a48351e4b05001a774a89f
, we can add a user to it as below:
curl -X PUT \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{
"users": {
"__op": "AddRelation",
"objects": [
{
"__type": "Pointer",
"className": "_User",
"objectId": "55a4800fe4b05001a7745c41"
}
]
}
}' \
https://{{host}}/1.1/roles/55a48351e4b05001a774a89f
Similarly, to remove a child role:
curl -X PUT \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{
"roles": {
"__op": "RemoveRelation",
"objects": [
{
"__type": "Pointer",
"className": "_Role",
"objectId": "55a483f0e4b05001a774b837"
}
]
}
}' \
https://{{host}}/1.1/roles/55a48351e4b05001a774a89f
Querying Roles
To find the roles a user belongs to:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'where={"users": {"__type": "Pointer", "className": "_User", "objectId": "5e03100ed4b56c008e4a91dc"}}' \
https://{{host}}/1.1/roles
To find the users contained in a role (users contained in sub-roles not counted):
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode '"$relatedTo":{"object":{"__type":"Pointer","className":"_Role","objectId":"5f3dea7b7a53400006b13886"},"key":"users"}' \
https://{{host}}/1.1/users
You can also query roles based on other attributes, just like querying a normal object.
Deleting Roles
Deleting roles is similar to delete objects.
It is authenticated with the X-LC-Session
HTTP header.
The session token passed in must belong to a user who has the permission to delete the specified role.
curl -X DELETE \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "X-LC-Session: qmdj8pdidnmyzp0c7yqil91oc" \
https://{{host}}/1.1/roles/55a483f0e4b05001a774b837
Roles and ACL
As demonstrated above, accessing data via REST API is also restricted by ACL, just as SDKs.
Roles make maintaining ACL easier. For example, to set an ACL of an object with the following permissions:
- It can be read by
Staff
s. - It can only be written by
Manager
s and its creator.
{
"55a4800fe4b05001a7745c41": {
"write": true
},
"role:Staff": {
"read": true
},
"role:Manager": {
"write": true
}
}
The creator belongs to the Staff
role, and the Manager
role is a child role of the Staff
role.
Therefore, since they will inherit read permissions, we did not grant them the read permission manually.
Let's look at another example of permission inherence among roles.
In UGC applications such as forums, Administrators
typically have all the permissions of Moderators
.
Thus Administrators
should be a sub-role of Moderators
.
curl -X PUT \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{
"roles": {
"__op": "AddRelation",
"objects": [
{
"__type": "Pointer",
"className": "_Role",
"objectId": "<AdministratorsRoleObjectId>"
}
]
}
}' \
https://{{host}}/1.1/roles/<ModeratorsRoleObjectId>
Files
Creating Files
The REST API does not support uploading files. Please use an SDK or the CLI to upload files.
If you already have a URL for a file, you can create a file by adding an entry like this:
curl -X POST \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/foo.jpg", "name": "foo.jpg", "mime_type": "image/jpeg"}' \
https://{{host}}/1.1/files
Associating With Objects
As mentioned above, files can be considered as a special form of pointers.
To associate a file object with an object, we just pass the file object {"id": "objectId of the file", "__type": "File"}
to an attribute of that file.
For example, to create a Staff
object with a photo:
curl -X POST \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{
"name": "tom",
"picture": {
"id": "543cbaede4b07db196f50f3c",
"__type": "File"
}
}' \
https://{{host}}/1.1/classes/Staff
Here id
is the objectId of the file.
Deleting Files
Deleting files is similar to deleting objects:
curl -X DELETE \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
https://{{host}}/1.1/files/543cbaede4b07db196f50f3c
Schema
You can use REST API to fetch the data schema of your application. For security concerns, the master key is required to fetch data schema.
To fetch the schema of all classes:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{masterkey}},master" \
https://{{host}}/1.1/schemas
Result:
{
"_User": {
"username": { "type": "String" },
"password": { "type": "String" },
"objectId": { "type": "String" },
"emailVerified": { "type": "Boolean" },
"email": { "type": "String" },
"createdAt": { "type": "Date" },
"updatedAt": { "type": "Date" },
"authData": { "type": "Object" }
}
// other classes
}
You can also fetch a single class's schema:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{masterkey}},master" \
https://{{host}}/1.1/schemas/_User
Data schema can be used with tools such as code generators and internal management interfaces.
Exporting Your Data
For security concerns, master key is required to export your data:
curl -X POST \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{masterkey}},master" \
-H "Content-Type: application/json" \
-d '{}' \
https://{{host}}/1.1/exportData
To specify date range (updatedAt
) of data to export:
curl -X POST \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{masterkey}},master" \
-H "Content-Type: application/json" \
-d '{"from_date":"2015-09-20", "to_date":"2015-09-25"}' \
https://{{host}}/1.1/exportData
To specify classes of data to export:
curl -X POST \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{masterkey}},master" \
-H "Content-Type: application/json" \
-d '{"classes":"_User,GameScore,Post"}' \
https://{{host}}/1.1/exportData
Just export the schema (no data will be exported):
curl -X POST \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{masterkey}},master" \
-H "Content-Type: application/json" \
-d '{"only-schema":"true"}' \
https://{{host}}/1.1/exportData
The exported schema file can be imported into other applications via the import data function on the dashboard.
After the data is exported, we will send an email to the application creator, containing the URL to download the data. You can also specify the address to receive this email:
curl -X POST \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{masterkey}},master" \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]"}' \
https://{{host}}/1.1/exportData
The export data job id will be returned:
{
"status": "running",
"id": "1wugzx81LvS5R4RHsuaeMPKlJqFMFyLwYDNcx6LvCc6MEzQ2",
"app_id": "{{appid}}"
}
You can also query the export data job status via the id returned previously:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{masterkey}},master" \
https://{{host}}/1.1/exportData/1wugzx81LvS5R4RHsuaeMPKlJqFMFyLwYDNcx6LvCc6MEzQ2
If the job status is done
, the download url will also be returned:
{
"status": "done",
"download_url": "https://download.leancloud.cn/export/example.tar.gz",
"id": "1wugzx81LvS5R4RHsuaeMPKlJqFMFyLwYDNcx6LvCc6MEzQ2",
"app_id": "{{appid}}"
}
If the job status is still running
, you can query it again later.
Other
Server Time
To retrieve the server's current time:
curl -i -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
https://{{host}}/1.1/date
The returned date is in UTC:
{
"iso": "2015-08-27T07:38:33.643Z",
"__type": "Date"
}
CORS Workarounds
You can wrap GET, PUT, and DELETE requests in a POST request:
- Specify the intended HTTP method in the
_method
parameter. - Specify
appid
andappkey
in_ApplicationId
and_ApplicationKey
parameters.
This is a workaround that only works for certain platforms. It is recommended to follow the HTML CORS standard instead.
GET
curl -i -X POST \
-H "Content-Type: text/plain" \
-d \
'{"_method":"GET",
"_ApplicationId":"{{appid}}",
"_ApplicationKey":"{{appkey}}"}' \
https://{{host}}/1.1/classes/Post/<objectId>
PUT
curl -i -X POST \
-H "Content-Type: text/plain" \
-d \
'{"_method":"PUT",
"_ApplicationId":"{{appid}}",
"_ApplicationKey":"{{appkey}}",
"upvotes":99}' \
https://{{host}}/1.1/classes/Post/<objectId>
DELETE
curl -i -X POST \
-H "Content-Type: text/plain" \
-d \
'{"_method": "DELETE",
"_ApplicationId":"{{appid}}",
"_ApplicationKey":"{{appkey}}"}' \
https://{{host}}/1.1/classes/Post/<objectId>