Format of requests and responses

Requests and responses are all in JSON format, so the mimetype is application/json. Ensure that requests you make that require a body (PATCH and POST requests) have the header Content-Type: application/json; if they do not, the server will respond with a 415 Unsupported Media Type.

Suppose we have the following Flask-SQLAlchemy models (the example works with pure SQLALchemy just the same):

from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)

class Person(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.Unicode, unique=True)
    birth_date = db.Column(db.Date)
    computers = db.relationship('Computer',
                                backref=db.backref('owner',
                                                   lazy='dynamic'))

class Computer(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.Unicode, unique=True)
    vendor = db.Column(db.Unicode)
    owner_id = db.Column(db.Integer, db.ForeignKey('person.id'))
    purchase_time = db.Column(db.DateTime)

Also suppose we have registered an API for these models at /api/person and /api/computer, respectively.

Note

API endpoints do not have trailing slashes. A request to, for example, /api/person/ will result in a 404 Not Found response.

Note

For all requests that would return a list of results, the top-level JSON object is a mapping from "objects" to the list. JSON lists are not sent as top-level objects for security reasons. For more information, see this.

GET /api/person

Gets a list of all Person objects.

Sample response:

HTTP/1.1 200 OK

{
  "num_results": 8,
  "total_pages": 3,
  "page": 2,
  "objects": [{"id": 1, "name": "Jeffrey", "age": 24}, ...]
}
GET /api/person?q=<searchjson>

Gets a list of all Person objects which meet the criteria of the specified search. For more information on the format of the value of the q parameter, see Making search queries.

Sample response:

HTTP/1.1 200 OK

{
   "num_results": 8,
   "total_pages": 3,
   "page": 2,
   "objects": [{"id": 1, "name": "Jeffrey", "age": 24}, ...]
 }
GET /api/person/(int: id)

Gets a single instance of Person with the specified ID.

Sample response:

HTTP/1.1 200 OK

{"id": 1, "name": "Jeffrey", "age": 24}
GET /api/person/(int: id)/computers

Gets a list of all Computer objects which are owned by the Person object with the specified ID.

Sample response:

HTTP/1.1 200 OK

{
  "num_results": 2,
  "total_pages": 1,
  "page": 1,
  "objects": [{"id": 1, "vendor": "Apple", "name": "MacBook", ...}, ...]
}
DELETE /api/person/(int: id)

Deletes the instance of Person with the specified ID.

Sample response:

HTTP/1.1 204 No Content
DELETE /api/person/(int: id)/computers/(int: id)

Removes the instance of Computer with the specified ID from the computers collection of the instance of Person with the specified ID. This is essentially a shortcut to using a PATCH /api/person/(int:id)/computers request with a remove parameter in the body of the request.

Sample response:

HTTP/1.1 204 No Content
POST /api/person

Creates a new person with initial attributes specified as a JSON string in the body of the request.

Sample request:

POST /api/person HTTP/1.1
Host: example.com

{"name": "Jeffrey", "age": 24}

Sample response:

HTTP/1.1 201 Created

{
  "id": 1,
  "name": "Jeffrey",
  "age" 24,
  "computers": []
}

The server will respond with 400 Bad Request if the request specifies a field which does not exist on the model.

To create a new person which includes a related list of new computer instances via a one-to-many relationship, a request must take the following form.

Sample request:

POST /api/person HTTP/1.1
Host: example.com

{
  "name": "Jeffrey",
  "age": 24,
  "computers":
    [
      {"manufacturer": "Dell", "model": "Inspiron"},
      {"manufacturer": "Apple", "model": "MacBook"}
    ]
}

Sample response:

HTTP/1.1 201 Created

{
  "id": 1,
  "name": "Jeffrey",
  "age": 24,
  "computers":
    [
      {"id": 1, "manufacturer": "Dell", "model": "Inspiron"},
      {"id": 2, "manufacturer": "Apple", "model": "MacBook"}
    ]
}

Warning

The response does not denote that new instances have been created for the Computer models.

To create a new person which includes a single related new computer instance (via a one-to-one relationship), a request must take the following form.

Sample request:

POST /api/person HTTP/1.1
Host: example.com

{
  "name": "Jeffrey",
  "age": 24,
  "computer": {"manufacturer": "Dell", "model": "Inspiron"}
}

Sample response:

HTTP/1.1 201 Created

{
  "name": "Jeffrey",
  "age": 24,
  "id": 1,
  "computer": {"id": 1, "manufacturer": "Dell", "model": "Inspiron"}
}

Warning

The response does not denote that a new Computer instance has been created.

To create a new person which includes a related list of existing computer instances via a one-to-many relationship, a request must take the following form.

Sample request:

POST /api/person HTTP/1.1
Host: example.com

{
  "name": "Jeffrey",
  "age": 24,
  "computers": [ {"id": 1}, {"id": 2} ]
}

Sample response:

HTTP/1.1 201 Created

{
  "id": 1,
  "name": "Jeffrey",
  "age": 24,
  "computers":
    [
      {"id": 1, "manufacturer": "Dell", "model": "Inspiron"},
      {"id": 2, "manufacturer": "Apple", "model": "MacBook"}
    ]
}

To create a new person which includes a single related existing computer instance (via a one-to-one relationship), a request must take the following form.

Sample request:

POST /api/person HTTP/1.1
Host: example.com

{
  "name": "Jeffrey",
  "age": 24,
  "computer": {"id": 1}
}

Sample response:

HTTP/1.1 201 Created

{
  "name": "Jeffrey",
  "age": 24,
  "id": 1,
  "computer": {"id": 1, "manufacturer": "Dell", "model": "Inspiron"}
}
PATCH /api/person
PUT /api/person

Sets specified attributes on every instance of Person which meets the search criteria described in the q parameter.

The JSON object specified in the body of a PATCH request to this endpoint may include a mapping from q to the parameters for a search, as described in Making search queries. If no q key exists, then all instances of the model will be patched.

PUT /api/person is an alias for PATCH /api/person, because the latter is more semantically correct but the former is part of the core HTTP standard.

The response will return a JSON object which specifies the number of instances in the Person database which were modified.

Sample request:

Suppose the database contains exactly three people with the letter “y” in his or her name.

PATCH /api/person HTTP/1.1
Host: example.com

{
  "age": 1,
  "q": {"filters": [{"name": "name", "op": "like", "val": "%y%"}]}
}

Sample response:

HTTP/1.1 200 OK

{"num_modified": 3}
PATCH /api/person/(int: id)
PUT /api/person/(int: id)

Sets specified attributes on the instance of Person with the specified ID number. PUT /api/person/1 is an alias for PATCH /api/person/1, because the latter is more semantically correct but the former is part of the core HTTP standard.

Sample request:

PATCH /api/person/1 HTTP/1.1
Host: example.com

{"name": "Foobar"}

Sample response:

HTTP/1.1 200 OK

{"id": 1, "name": "Foobar", "age": 24}

The server will respond with 400 Bad Request if the request specifies a field which does not exist on the model.

To add a list of existing objects to a one-to-many relationship, a request must take the following form.

Sample request:

PATCH /api/person/1 HTTP/1.1
Host: example.com

{ "computers":
  {
    "add": [ {"id": 1} ]
  }
}

Sample response:

HTTP/1.1 200 OK

{
  "id": 1,
  "name": "Jeffrey",
  "age": 24,
  "computers": [ {"id": 1, "manufacturer": "Dell", "model": "Inspiron"} ]
}

To add a list of new objects to a one-to-many relationship, a request must take the following form.

Sample request:

PATCH /api/person/1 HTTP/1.1
Host: example.com

{ "computers":
  {
    "add": [ {"manufacturer": "Dell", "model": "Inspiron"} ]
  }
}

Warning

The response does not denote that a new instance has been created for the Computer model.

Sample response:

HTTP/1.1 200 OK

{
  "id": 1,
  "name": "Jeffrey",
  "age": 24,
  "computers": [ {"id": 1, "manufacturer": "Dell", "model": "Inspiron"} ]
}

Similarly, to add a new or existing instance of a related model to a one-to-one relationship, a request must take the following form.

Sample request:

PATCH /api/person/1 HTTP/1.1
Host: example.com

{ "computers":
  {
    "add": {"id": 1}
  }
}

Sample response:

HTTP/1.1 200 OK

{
  "id": 1,
  "name": "Jeffrey",
  "age": 24,
  "computers": [ {"id": 1, "manufacturer": "Dell", "model": "Inspiron"} ]
}

To remove an existing object (without deleting that object from its own database) from a one-to-many relationship, a request must take the following form.

Sample request:

PATCH /api/person/1 HTTP/1.1
Host: example.com

{ "computers":
  {
    "remove": [ {"id": 2} ]
  }
}

Sample response:

HTTP/1.1 200 OK

{
  "id": 1,
  "name": "Jeffrey",
  "age": 24,
  "computers": [
    {"id": 1, "manufacturer": "Dell", "model": "Inspiron 9300"},
    {"id": 3, "manufacturer": "Apple", "model": "MacBook"}
  ]
}

To remove an existing object from a one-to-many relationship and additionally delete it from its own database, a request must take the following form.

Sample request:

PATCH /api/person/1 HTTP/1.1
Host: example.com

{ "computers":
  {
    "remove": [ {"id": 2, "__delete__": true} ]
  }
}

Warning

The response does not denote that the instance was deleted from its own database.

Sample response:

HTTP/1.1 200 OK

{
  "id": 1,
  "name": "Jeffrey",
  "age": 24,
  "computers": [
    {"id": 1, "manufacturer": "Dell", "model": "Inspiron 9300"},
    {"id": 3, "manufacturer": "Apple", "model": "MacBook"}
  ]
}

To set the value of a one-to-many relationship to contain either existing or new instances of the related model, a request must take the following form.

Sample request:

PATCH /api/person/1 HTTP/1.1
Host: example.com

{ "computers":
    [
      {"id": 1},
      {"id": 3},
      {"manufacturer": "Lenovo", "model": "ThinkPad"}
    ]
}

Sample response:

HTTP/1.1 200 OK

{
  "id": 1,
  "name": "Jeffrey",
  "age": 24,
  "computers": [
    {"id": 1, "manufacturer": "Dell", "model": "Inspiron 9300"},
    {"id": 3, "manufacturer": "Apple", "model": "MacBook"}
    {"id": 4, "manufacturer": "Lenovo", "model": "ThinkPad"}
  ]
}

To set the value of a one-to-many relationship and update fields on existing instances of the related model, a request must take the following form.

Suppose the Person instance looked like this before the sample PATCH request below:

HTTP/1.1 200 OK

{
  "id": 1,
  "name": "Jeffrey",
  "age": 24,
  "computers": [
    {"id": 1, "manufacturer": "Apple", "model": "MacBook"}
  ]
}

Sample request:

PATCH /api/person/1 HTTP/1.1
Host: example.com

{ "computers":
    [
      {"id": 1, "manufacturer": "Lenovo", "model": "ThinkPad"}
    ]
}

Sample response:

HTTP/1.1 200 OK

{
  "id": 1,
  "name": "Jeffrey",
  "age": 24,
  "computers": [
    {"id": 1, "manufacturer": "Lenovo", "model": "ThinkPad"}
  ]
}

The changes reflected in this response have been made to the Computer instance with ID 1.

Date and time fields

Flask-Restless will automatically parse and convert date and time strings into the corresponding Python objects. Flask-Restless also understands intervals (also known as durations), if you specify the interval as an integer representing the number of seconds that the interval spans.

If you want the server to set the value of a date or time field of a model as the current time (as measured at the server), use one of the special strings "CURRENT_TIMESTAMP", "CURRENT_DATE", or "LOCALTIMESTAMP". When the server receives one of these strings in a request, it will use the corresponding SQL function to set the date or time of the field in the model.

Errors and error messages

Most errors return 400 Bad Request. A bad request, for example, will receive a response like this:

HTTP/1.1 400 Bad Request

{"message": "Unable to decode data"}

If your request triggers a SQLAlchemy DataError, IntegrityError, or ProgrammingError, the session will be rolled back.

Function evaluation

If the allow_functions keyword argument is set to True when creating an API for a model using APIManager.create_api(), then an endpoint will be made available for GET /api/eval/person which responds to requests for evaluation of functions on all instances the model.

Sample request:

GET /api/eval/person?q={"functions": [{"name": "sum", "field": "age"}, {"name": "avg", "field": "height"}]} HTTP/1.1

The format of the response is

HTTP/1.1 200 OK

{"sum__age": 100, "avg_height": 68}

If no functions are specified in the request, the response will contain the empty JSON object, {}.

Note

The functions whose names are given in the request will be evaluated using SQLAlchemy’s func object.

Example

To get the total number of rows in the query (that is, the number of instances of the requested model), use count as the name of the function to evaluate, and id for the field on which to evaluate it:

Request:

GET /api/eval/person?q={"functions": [{"name": "count", "field": "id"}]} HTTP/1.1

Response:

HTTP/1.1 200 OK

{"count__id": 5}

JSONP callbacks

Add a callback=myfunc query parameter to the request URL on any GET requests (including endpoints for function evaluation) to have the JSON data of the response wrapped in the Javascript function myfunc. This can be used to circumvent some cross domain scripting security issues. For example, a request like this:

GET /api/person/1?callback=foo HTTP/1.1

will produce a response like this:

HTTP/1.1 200 OK

foo({"meta": ..., "data": ...})

Then in your Javascript code, write the function foo like this:

function foo(response) {
  var meta, data;
  meta = response.meta;
  data = response.data;
  // Do something cool here...
}

The metadata includes the status code and the values of the HTTP headers, including the Link headers parsed in JSON format. For example, a link that looks like this:

Link: <url1>; rel="next", <url2>; rel="foo"; bar="baz"

will look like this in the JSON metadata:

[
  {"url": "url1", "rel": "next"},
  {"url": "url2", "rel": "foo", "bar": "baz"}
]

The mimetype of a JSONP response is application/javascript instead of the usual application/json, because the payload of such a response is not valid JSON.