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
(:http:method:`patch` and :http:method:`post` requests) have the header
Content-Type: application/json
; if they do not, the server will respond
with a :http:statuscode:`415`.
Suppose we have the following Flask-SQLAlchemy models (the example works with pure SQLALchemy just the same):
from flask import Flask
from flask_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 :http:statuscode:`404` 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.
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 :http:statuscode:`400`. 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 :http: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
:http:method:`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.
Pagination¶
Responses to :http:method:`get` requests are paginated by default, with at most
ten objects per page. To request a specific page, add a page=N
query
parameter to the request URL, where N
is a positive integer (the first page
is page one). If no page
query parameter is specified, the first page will
be returned.
In order to specify the number of results per page, add the query parameter
results_per_page=N
where N
is a positive integer. If
results_per_page
is greater than the maximum number of results per page as
configured by the server (see Server-side pagination), then the query
parameter will be ignored.
In addition to the "objects"
list, the response JSON object will have a
"page"
key whose value is the current page, a "num_pages"
key whose
value is the total number of pages into which the set of matching instances is
divided, and a "num_results"
key whose value is the total number of
instances which match the requested search. For example, a request to
:http:get:`/api/person?page=2` will result in the following response:
HTTP/1.1 200 OK
{
"num_results": 8,
"page": 2,
"num_pages": 3,
"objects": [{"id": 1, "name": "Jeffrey", "age": 24}, ...]
}
If pagination is disabled (by setting results_per_page=None
in
APIManager.create_api()
, for example), any page
key in the query
parameters will be ignored, and the response JSON will include a "page"
key
which always has the value 1
.
Note
As specified in in Query format, clients can receive responses with
limit
(a maximum number of objects in the response) and offset
(the
number of initial objects to skip in the response) applied. It is possible,
though not recommended, to use pagination in addition to limit
and
offset
. For simple clients, pagination should be fine.