Tutorial: Requesting database in REST

REST API Requesting database in REST

Prerequisites

  • Create an account on [[service]]
  • Create an application (or namespace), in this documentation we will use the fictional test-rest application
  • Have the curl command available (or another tool to build HTTP requests)

Look at the following sample, which makes it possible to browse data level per level: https://io.datasync.orange.com/samples/tree/.

Write Data

WARNING: Security rules concerns are explained later in this document!

Writing data is as easy as sending a PUT request (this request is equivalent to the set() method):

$ curl -X PUT -H 'Content-Type: application/json' "[[baseUrl]]/datasync/v2/<application>/data/<path/of/your/data>" -d '<JSON data>'

Command example:

$ curl -X PUT -H 'Content-Type: application/json' "[[baseUrl]]/datasync/v2/test-rest/data/main/sub/infos" -d '{"id": 22, "version": 42, "detail": "this is a test", "level2": {"level21": "22", "level22": true}}'

The server answers with the "204 No Content" HTTP status.

After sending the request, you should see your data in the data tab of the [[console]]. Notice that the main / sub / infos path levels (or path segments) have been created automatically. Be careful, some characters are forbidden in path levels (. $ [ ] # /), and no more than 32 levels are authorized in the database (comprehensive limitations can be found here).

In order to store new element data into an ordered list at a given level, use the HTTP POST method instead of PUT (this request is equivalent to the push() method). It makes the [[service]] server create a new level in your tree, with a unique and temporally ordered name.

Command example:

$ curl -X POST -H 'Content-Type: application/json' "[[baseUrl]]/datasync/v2/test-rest/data/history/main/sub" -d '{"id": 22, "version": 42, "detail": "this is a test", "level2": {"level21": "22", "level22": true}}'

In this case, the server answers with the "201 Created" HTTP status, and returns the path of the newly created level within the "Location" header.

Server response:

HTTP/1.1 201 Created
Location: [[baseUrl]]/datasync/v2/test-rest/data/history/main/sub/-KolzsgbVnMc4s7lr-c-

Read Data

To retrieve data, a simple GET is needed:

$ curl –X GET "[[baseUrl]]/datasync/v2/<application>/data/<path/of/your/data>"

Command example:

$ curl -X GET "[[baseUrl]]/datasync/v2/test-rest/data/main/sub/infos"

The server answers with the "200 OK" HTTP status and provides the following content:

{"id":22,"version":42,"detail":"this is a test","level2":{"level21":"22","level22":true}}

Other command example, which applies to a sub-level:

$ curl -X GET "[[baseUrl]]/datasync/v2/test-rest/data/main/sub/infos/level2"

Server response content:

{"level21":"22","level22":true}

Other command example, which applies to the whole database:

$ curl -X GET "[[baseUrl]]/datasync/v2/test-rest/data"

Server response content:

{"main":{"sub":{"infos":{"id":22,"version":42,"detail":"this  is a  test","level2":{"level21":"22","level22":true}}}},"history":{"main":{"sub":{"-KolzsgbVnMc4s7lr-c-":{"id":22,"version":42,"detail":"this  is a test","level2":{"level21":"22","level22":true}}}}}}

Server can format the data returned within the response content in a pretty way, using the print=pretty parameter:

Command example, requesting the whole database in a pretty format:

$ curl -X GET "[[baseUrl]]/datasync/v2/test-rest/data?print=pretty"

Server response content:

{
  "main": {
    "sub": {
      "infos": {
        "id": 22,
        "version": 42,
        "detail": "this is a test",
        "level2": {
          "level21": "22",
          "level22": true
        }
      }
    }
  },
  "history": {
    "main": {
      "sub": {
        "-KolzsgbVnMc4s7lr-c-": {
          "id": 22,
          "version": 42,
          "detail": "this is a test",
          "level2": {
            "level21": "22",
            "level22": true
          }
        }
      }
    }
  }
}

Tips for further needs

When your database gets bigger, a GET request at the root level may result in a too_big error. In such a case, you can add the shallow=true parameter to your GET request. This parameter makes the request never end in a too_big error by returning only the actual sub-levels of the requested path, without their attached data.

Example:

$ curl -X GET "[[baseUrl]]/datasync/v2/test-rest/data?shallow=true&print=pretty"

Server response content:

{
  "main": true,
  "history": true
}

Security of your data

Easy way with a secret key

By default, all your data is entirely accessible from the Web for all people knowing the name of your [[service]] application. In order to secure access to your data, you have to define security rules for each part of your data model within the security tab of your application in the [[console]]. These rules are then permanently checked by [[service]] servers for each attempt of read or write access.

A very basic test to start playing with security rules consists in removing all read and write rights, using the following security rules:

{
  "rules": {
    ".read": false,
    ".write": false
  }
}

Then, if you try a GET (or PUT) request, it will end in a permission denied:
{"error":{"message":"Permission denied Get on /","code":"permission_denied"},"status":"permission_denied","success":false}

To enable your application to send data to your [[service]] database, we will use a secret key. This key allows the read/write actions on the data even if security rules forbid them (it's like a "root" right, so be careful: With great power, comes great responsibility).

Go to the authentication tab of the [[console]]. Add a new key with the add a secret button (always consider generating a new key before attempting to revoke the default one) and copy it using the show button.

Now you just have to put the key prefixed with "Bearer " in the Authorization header of the HTTP requests to access your data:

curl -X PUT "[[baseUrl]]/datasync/v2/test-rest/data" -H "Authorization: Bearer xxxxxxxxxxxxxxxxxxxxx" -H "Content-Type: application/json" -d '{"id": 22, "version": 42, "firmware": "1.3.4.2", "level2": {"level21": "22", "level22": true}}'

Or for a GET:

curl -X GET "[[baseUrl]]/datasync/v2/test-rest/data" -H "Authorization: Bearer xxxxxxxxxxxxxxxxxxxxx"

If the key is compromised, you can revoke it in the [[console]] (authentication tab, show button, then revoke button).

A simple security mode for a web application that only shows some data that are produced by a backend server (for which we can imagine that the secret key will be well secured), is to forbid write actions (".write" : false in the security rules) and to authorize all people for read actions (".read" : true in the security rules). The main drawback is that someone that knows the identifier of your [[service]] application and how [[service]] works can easily retrieve all your data.

You can also forbid reading at the root level and authorize reading only at some explicit paths of the application data. For instance in our test case, it could be something like:

{
  "rules": {
    ".read": false,
    ".write": false,
    "history": {
      ".read": true
    },
    "main": {
      ".read": true
    }
  }
}

Here, a GET request at root level will fail whereas a GET request at the /history path will work. Beware it's not enough to secure your data. The best way is to rely on users authentication methods provided by [[service]] and then to define related security rules. Please have a look at the Authentication and Security detailed documentation pages.