REST API FOR HUMANS™eve
eve
nicolaiarocci
CoFounder and Lead Dev @C2K
Open Source • MongoDB Master • Speaking • CoderDojo • PSF
PHILOSOPHY
YOU HAVE DATA
STORED SOMEWHERE
YOU NEED
A FULL FEATURED REST API
TO EXPOSE YOUR DATA
PIP INSTALL EVE
TO GET A FEATURE RICH RESTFUL WEB API FOR FREE
POWERED BY
QUICKSTART
#1 run.py
from eve import Eve
app = Eve()
!
if __name__ == '__main__':
app.run()
#2 settings.py
# just a couple API endpoints with no custom
# schema or rules. Will just dump from people
# and books db collections
!
DOMAIN = {
‘people’: {}
‘books’: {}
}
#3 launch the API
$ python run.py
* Running on http://127.0.0.1:5000/
#4 enjoy
$ curl -i http://127.0.0.1:5000/people
{
"_items": [],
"_links": {
"self": {
"href": "127.0.0.1:5000/people",
"title": "people" },
"parent": {
"href": "127.0.0.1:5000",
"title": "home"}
}
}
#4 enjoy
$ curl -i http://127.0.0.1:5000/people
{
"_items": [],
"_links": {
"self": {
"href": "127.0.0.1:5000/people",
"title": “people" },
"parent": {
"href": "127.0.0.1:5000",
"title": “home"}
}
}
HATEOAS AT WORK HERE
#4 enjoy
$ curl -i http://127.0.0.1:5000/people
{
"_items": [],
"_links": {
"self": {
"href": "127.0.0.1:5000/people",
"title": "people" },
"parent": {
"href": "127.0.0.1:5000",
"title": "home"}
}
}
CLIENTS CAN EXPLORE THE API PROGRAMMATICALLY
#4 enjoy
$ curl -i http://127.0.0.1:5000/people
{
"_items": [],
"_links": {
"self": {
"href": "127.0.0.1:5000/people",
"title": "people" },
"parent": {
"href": "127.0.0.1:5000",
"title": "home"}
}
}
AND EVENTUALLY FILL THEIR UI
#4 enjoy
$ curl -i http://127.0.0.1:5000/people
{
"_items": [],
"_links": {
"self": {
"href": "127.0.0.1:5000/people",
"title": "people" },
"parent": {
"href": "127.0.0.1:5000",
"title": "home"}
}
}
EMTPY RESOURCE AS WE DIDN’T CONNECT ANY DATASOURCE
settings.py
# let’s connect to a mongo instance
!
MONGO_HOST = 'localhost'
MONGO_PORT = 27017
MONGO_USERNAME = 'user'
MONGO_PASSWORD = 'user'
MONGO_DBNAME = ‘apitest'
settings.py
# let’s also add some validation rules
!
DOMAIN['people']['schema'] = {
'name': {
'type': 'string',
'maxlength': 50,
'unique': True}
'email': {
'type': 'string',
'regex': '^S+@S+$'},
'location': {
'type': 'dict',
'schema': {
'address': {'type': 'string'},
'city': {'type': 'string'}}},
'born': {'type': ‘datetime'}}
settings.py
# let’s also add some validation rules
!
DOMAIN['people']['schema'] = {
'name': {
'type': 'string',
'maxlength': 50,
'unique': True}
'email': {
'type': 'string',
'regex': '^S+@S+$'},
'location': {
'type': 'dict',
'schema': {
'address': {'type': 'string'},
'city': {'type': 'string'}}},
'born': {'type': ‘datetime'}}
THIS REGEX SUCKS. DON’T USE IN PRODUCTION
settings.py
# allow write access to API endpoints
# (default is [‘GET’] for both settings)
!
# /people
RESOURCE_METHODS = ['GET','POST']
!
# /people/<id>
ITEM_METHODS = ['GET','PATCH','PUT','DELETE']
settings.py
# allow write access to API endpoints
# (default is [‘GET’] for both settings)
!
# /people
RESOURCE_METHODS = ['GET','POST']
!
# /people/<id>
ITEM_METHODS = ['GET','PATCH','PUT','DELETE']
ADD/CREATE ONE OR MORE ITEMS
settings.py
# allow write access to API endpoints
# (default is [‘GET’] for both settings)
!
# /people
RESOURCE_METHODS = ['GET', 'POST']
!
# /people/<id>
ITEM_METHODS = ['GET','PATCH','PUT','DELETE']
EDIT ITEM
settings.py
# allow write access to API endpoints
# (default is [‘GET’] for both settings)
!
# /people
RESOURCE_METHODS = ['GET', 'POST']
!
# /people/<id>
ITEM_METHODS = ['GET','PATCH','PUT','DELETE']
REPLACE ITEM
settings.py
# allow write access to API endpoints
# (default is [‘GET’] for both settings)
!
# /people
RESOURCE_METHODS = ['GET', 'POST']
!
# /people/<id>
ITEM_METHODS = ['GET','PATCH','PUT','DELETE']
YOU GUESSED IT
settings.py
# a few more config options
!
DOMAIN[‘people’].update(
{
‘item_title’: ‘person’,
‘cache_control’: ‘max-age=10,must-revalidate,
‘cache_expires’: 10,
‘additional_lookup’: {
‘url’: ‘regex)”[w]+”)’,
‘field’: ‘name’
}
)
FEATURES
MONGO FILTERS
?where={“lastname”: “Doe”}
PYTHON FILTERS
?where=lastname==“Doe”
SORTING
?sort=[(“total”: -1)]
SORT BY ‘TOTAL’, DESCENDING ORDER
PAGINATION
?max_results=20&page=2
MAX 20 RESULTS/PAGE; PAGE 2
PROJECTIONS
?projection={"avatar": 0}
RETURN ALL FIELDS BUT ‘AVATAR’
PROJECTIONS
?projection={"lastname": 1}
ONLY RETURN ‘LASTNAME’
EMBEDDED RESOURCES
?embedded={"author": 1}
NOT EMBEDDED
$ curl -i <url>
!
HTTP/1.1 200 OK
{

"title": "Book Title",

"description": "book description",

"author": “52da465a5610320002660f94"

}
RAW FOREIGN KEY
(DEFAULT)
EMBEDDED
$ curl -i <url>?embedded={“author”: 1}
!
HTTP/1.1 200 OK
{

"title": "Book Title",

"description": "book description",

"author": {

“firstname”: “Mark”,

“lastname”: “Green”,

}

}
REQUEST EMBEDDED AUTHOR
EMBEDDED
$ curl -i <url>?embedded={“author”: 1}
!
HTTP/1.1 200 OK
{

"title": "Book Title",

"description": "book description",

"author": {

“firstname”: “Mark”,

“lastname”: “Green”,

}

}
EMBEDDED DOCUMENT
JSON AND XML
BUILT-IN FOR ALL RESPONSES
APPLICATION/JSON
[
{
"firstname": "Mark",
"lastname": "Green",
"born": "Sat, 23 Feb 1985 12:00:00 GMT",
"role": ["copy", "author"],
"location": {"city": "New York", "address": "4925 Lacross Road"},
"_id": "50bf198338345b1c604faf31",
"_updated": "Wed, 05 Dec 2012 09:53:07 GMT",
"_created": "Wed, 05 Dec 2012 09:53:07 GMT",
"_etag": "ec5e8200b8fa0596afe9ca71a87f23e71ca30e2d",
},
{
"firstname": "John",
...
},
]
APPLICATION/JSON
[
{
"firstname": "Mark",
"lastname": "Green",
"born": "Sat, 23 Feb 1985 12:00:00 GMT",
"role": ["copy", "author"],
"location": {"city": "New York", "address": "4925 Lacross Road"},
"_id": "50bf198338345b1c604faf31",
"_updated": "Wed, 05 Dec 2012 09:53:07 GMT",
"_created": "Wed, 05 Dec 2012 09:53:07 GMT",
"_etag": "ec5e8200b8fa0596afe9ca71a87f23e71ca30e2d",
},
{
"firstname": "John",
...
},
]
METAFIELDS ARE CONFIGURABLE
APPLICATION/XML
<resource href=“localhost:5000/people" title="people">
<resource href="localhost:5000/people/<id>" title="person">
<lastname>Green</lastname>
<firstname>Mark</firstname>
<born>Wed, 05 Dec 2012 09:53:07 GMT</born>
<role>author</role>
<role>copy</role>
<location>
<address>4925 Lacross Road</address>
<city>New York</city>
</location>
<_id>50bf198338345b1c604faf31</_id>
<created>Wed, 05 Dec 2012 09:53:07 GMT</created>
<updated>Sat, 18 Jan 2014 09:16:10 GMT</updated>
<etag>ec5e8200b8fa0596afe9ca71a87f23e71ca30e2d</etag>
</resource>
...
<resource>
HATEOAS
HYPERMEDIA AS THE ENGINE OF APPLICATION STATE
HATEOAS
{
“_links”: {
“self”: {
“href”: “/people”,
“title”: “people”},
“parent”: {
“href”: “/”,
“title”: “home”},
“next”: {
“href”: “/people?page=2”,
“title”: “next page”},
“last”: {
“href: “/people?page=10”,
“title”: “last page”}
}
}
DOCUMENT VERSIONS
?version=3
?version=all
?version=diffs
FILE STORAGE
FILES ARE STORED IN GRIDFS BY DEFAULT
FILE STORAGE / SETTINGS
accounts = {

'name': {'type': 'string'},

'pic': {'type': 'media'},

…

}
FILE STORAGE
$ curl F “name=doe” —F “pic=@profile.jpg" <url>
HTTP/1.1 200 OK
!
$ curl -i <url>
HTTP/1.1 200 OK
{

"name": "john",

"pic": "/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAA…"

}
MULTIPART/DATA-FORM POST
FILE STORAGE
$ curl F “name=doe” —F “pic=@profile.jpg" <url>
HTTP/1.1 200 OK
!
$ curl -i <url>
HTTP/1.1 200 OK
{

"name": "john",

"pic": "/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAA…"

}
FILES RETURNED AS BASE64 STRINGS
FILE STORAGE (WITH META)
$ curl -i <url>
HTTP/1.1 200 OK
{

"name": "john",

"pic": {
“file”: ”/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAA”,
“content_type”: “image/jpeg”,
“name”: “profile.jpg”,
“length”: 8129
}

}
EXTENDED_MEDIA_INFO: TRUE
RATE LIMITING
POWERED
RATE LIMITING / SETTINGS
# Rate limit on GET requests:
# 1 requests 1 minute window (per client)
!
RATE_LIMIT_GET = (1, 60)
RATE LIMITING / GET #1
$ curl -i <url>
!
HTTP/1.1 200 OK
X-RateLimit-Limit: 1
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1390486659
RATE LIMITING / GET #2
$ curl -i <url>
!
HTTP/1.1 429 TOO MANY REQUESTS
CONDITIONAL REQUESTS
ALLOW CLIENTS TO ONLY REQUEST
NON-CACHED CONTENT
IF-MODIFIED-SINCE
If-Modified-Since: Wed, 05 Dec 2012 09:53:07 GMT
“Please return modified data since <date> or 304”
IF-NONE-MATCH
If-None-Match:1234567890123456789012345678901234567890
“Please return data if it has changed or 304”
>
BULK INSERTS
INSERT MULTIPLE DOCUMENTS WITH A SINGLE REQUEST
BULK INSERTS / REQUEST
$ curl -d ‘
[
{
"firstname": "barack",
"lastname": “obama"
},
{
"firstname": "mitt",
"lastname": “romney”
}
]'
-H 'Content-Type: application/json’ <url>
BULK INSERTS / RESPONSE
[
{
"_status": "OK",
"_updated": "Thu, 22 Nov 2012 15:22:27 GMT",
"_id": "50ae43339fa12500024def5b",
"_etag": "749093d334ebd05cf7f2b7dbfb7868605578db2c"
"_links": {"self": {"href": “<url>”, "title": "person"}}
},
{
"_status": "OK",
"_updated": "Thu, 22 Nov 2012 15:22:27 GMT",
"_id": "50ae43339fa12500024def5c",
"_etag": "62d356f623c7d9dc864ffa5facc47dced4ba6907"
"_links": {"self": {"href": “<url>", "title": "person"}}
}
]
COHERENCE MODE OFF: ONLY META FIELDS ARE RETURNED
BULK INSERTS / RESPONSE
[
{
"_status": "OK",
"_updated": "Thu, 22 Nov 2012 15:22:27 GMT",
"_id": "50ae43339fa12500024def5b",
"_etag": "749093d334ebd05cf7f2b7dbfb7868605578db2c"
"_links": {"self": {"href": “<url>”, "title": “person”}},
"firstname": "barack",
"lastname": "obama",
!
},
{
"_status": "OK",
"_updated": "Thu, 22 Nov 2012 15:22:27 GMT",
"_id": "50ae43339fa12500024def5c",
"_etag": "62d356f623c7d9dc864ffa5facc47dced4ba6907"
"_links": {"self": {"href": “<url>", "title": "person"}}
"firstname": "mitt",
"lastname": "romney",
}
]
COHERENCE MODE ON: ALL FIELDS RETURNED
DATA INTEGRITY
CONCURRENCY CONTROL
NO OVERWRITING DOCUMENTS
WITH OBSOLETE VERSIONS
DATA INTEGRITY / CONCURRENCY
$ curl -X PATCH -i <url> 

-d '{"firstname": "ronald"}'

HTTP/1.1 403 FORBIDDEN
IF-MATCH MISSING
DATA INTEGRITY / CONCURRENCY
$ curl -X PATCH -i <url>

-H "If-Match: <obsolete_etag>”

-d '{"firstname": “ronald”}'
!
HTTP/1.1 412 PRECONDITION FAILED

ETAG MISMATCH
DATA INTEGRITY / CONCURRENCY
$ curl -X PATCH -i <url>

-H “If-Match: 206fb4a39815cc0ebf48b2b52d7…”

-d '{"firstname": “ronald"}'
!
HTTP/1.1 200 OK
UPDATE ALLOWED IF CLIENT AND SERVER ETAG MATCH
DATA VALIDATION
[
{
"_status": "ERR",
"_issues": {“name”: “value ‘clinton’ not unique”}
},
{
"_status": “OK",
"_updated": "Thu, 22 Nov 2012 15:22:27 GMT”,
"_id": “50ae43339fa12500024def5c",
"_etag": “62d356f623c7d9dc864ffa5facc47dced4ba6907"
"_links": {
"self": {
"href": “<url>”,
"title": “person"
}
}
}
]
AUTHENTICATION
AND AUTHORIZATION
BASIC, TOKEN AND HMAC AUTH SUPPORTED
RUNS ON ALL PYTHONS
2.6 / 2.7 / 3.3 / 3.4 and PyPy
AND MORE
CORS. CACHE CONTROL. VERSONING AND MORE.
BSD LICENSED
TEAR IT APART
DEVELOPERS
CUSTOM DATA LAYERS
BUILD YOUR OWN DATA LAYER
SQL ALCHEMY (WIP)
SQLALCHEMY (WIP)
@registerSchema('invoices')
class Invoices(CommonColumns):
__tablename__ = 'invoices'
number = db.Column(db.Integer)
people = db.Column(db.Integer,
db.ForeignKey('people._id'))
ELASTICSERCH
MONGODB (DEFAULT)
AUTHENTICATION
BASIC | TOKEN | HMAC
SECURITY AT A GLANCE
• global authentication
• custom endpoint auth
• public enpoints and methods
• role based access control
• user restricted resource access
three steps
Auth
tutorial
#1
IMPORT BASE AUTH CLASS
#2
OVERRIDE CHECK_AUTH() METHOD
#3
PASS CUSTOM CLASS TO THE EVE APP
Done
CUSTOM VALIDATION
EXTEND THE BUILT-IN VALIDATION SYSTEM
CUSTOM VALIDATION
• add custom data types
• add custom validation logic
EVENT HOOKS
PLUG CUSTOM ACTIONS INTO THE API LOOP
EVENT HOOKS AT A GLANCE
• POST on_insert/on_inserted
• GET on_fetch/on_fetched
• PATCH on_update/on_updated
• PUT on_replace/on_replaced
• DELETE on_delete/on_deteled
• on_pre_<method>; on_post_<method>
TRANSFORM INCOMING DOCUMENTS
CUSTOM FILE STORAGE
custom MediaStorage subclasses to S3, File System, you name it
COMMUNITY
EVE-DOCS
GENERATES DOCUMENTATION FOR EVE APIS IN HTML AND JSON FORMATS
CHARLES FLYNN
EVE-DOCS
EVE-MONGOENGINE
ENABLES MONGOENGINE ORM MODELS TO BE USED AS EVE SCHEMA
STANISLAV HELLER
EVE-ELASTIC
ELASTICSEARCH DATA LAYER FOR EVE REST FRAMEWORK
PETR JASEK
EVE-MOCKER
MOCKING TOOL FOR EVE POWERED REST APIS
THOMAS SILEO
{48: <you name here>}
Bryan Cattle Christoph Witzany Daniele Pizzolli
dccrazyboy Dong Wei Ming Florian Rathgeber Francisco
Corrales Morales Garrin Kimmell Gianfranco Palumbo Jaroslav
Semančík Jean Boussier John Deng Jorge Puente Sarrín
Josh Villbrandt Julien Barbot Ken Carpenter Kevin
Bowrin Kracekumar Nicolas Bazire Nicolas Carlier Ondrej
Slinták Petr Jašek Paul Doucet Robert Wlodarczyk Roberto Pasini
Ronan Delacroix Roy Smith Ryan Shea Samuel Sutch
Stanislav Heller Thomas Sileo Tomasz Jezierski Xavi Cubillas
NOW COOKING
GeoJSON
Support and validation for GeoJSON types
!
Point, LineString, Polygon, MultiPoint,
MultiLineString, MultiPolygon, GeometricalCollection
JSONP*
IN CASE YOUR BROWSER/JS FRAMEWORK CANT HANDLE C.O.R.S.
* PENDING SECURITY REVIEW
JSON-LD / HAL / SIREN*
CURSTOM RENDER CLASSES
* MAYBE (UNDER CONSIDERATION)
python-eve.org
JOIN US
eve
nicolaiarocci
Thank you!

Eve - REST API for Humans™

  • 1.
    REST API FORHUMANS™eve eve
  • 2.
    nicolaiarocci CoFounder and LeadDev @C2K Open Source • MongoDB Master • Speaking • CoderDojo • PSF
  • 3.
  • 4.
  • 5.
    YOU NEED A FULLFEATURED REST API TO EXPOSE YOUR DATA
  • 6.
    PIP INSTALL EVE TOGET A FEATURE RICH RESTFUL WEB API FOR FREE
  • 7.
  • 11.
  • 12.
    #1 run.py from eveimport Eve app = Eve() ! if __name__ == '__main__': app.run()
  • 13.
    #2 settings.py # justa couple API endpoints with no custom # schema or rules. Will just dump from people # and books db collections ! DOMAIN = { ‘people’: {} ‘books’: {} }
  • 14.
    #3 launch theAPI $ python run.py * Running on http://127.0.0.1:5000/
  • 15.
    #4 enjoy $ curl-i http://127.0.0.1:5000/people { "_items": [], "_links": { "self": { "href": "127.0.0.1:5000/people", "title": "people" }, "parent": { "href": "127.0.0.1:5000", "title": "home"} } }
  • 16.
    #4 enjoy $ curl-i http://127.0.0.1:5000/people { "_items": [], "_links": { "self": { "href": "127.0.0.1:5000/people", "title": “people" }, "parent": { "href": "127.0.0.1:5000", "title": “home"} } } HATEOAS AT WORK HERE
  • 17.
    #4 enjoy $ curl-i http://127.0.0.1:5000/people { "_items": [], "_links": { "self": { "href": "127.0.0.1:5000/people", "title": "people" }, "parent": { "href": "127.0.0.1:5000", "title": "home"} } } CLIENTS CAN EXPLORE THE API PROGRAMMATICALLY
  • 18.
    #4 enjoy $ curl-i http://127.0.0.1:5000/people { "_items": [], "_links": { "self": { "href": "127.0.0.1:5000/people", "title": "people" }, "parent": { "href": "127.0.0.1:5000", "title": "home"} } } AND EVENTUALLY FILL THEIR UI
  • 19.
    #4 enjoy $ curl-i http://127.0.0.1:5000/people { "_items": [], "_links": { "self": { "href": "127.0.0.1:5000/people", "title": "people" }, "parent": { "href": "127.0.0.1:5000", "title": "home"} } } EMTPY RESOURCE AS WE DIDN’T CONNECT ANY DATASOURCE
  • 20.
    settings.py # let’s connectto a mongo instance ! MONGO_HOST = 'localhost' MONGO_PORT = 27017 MONGO_USERNAME = 'user' MONGO_PASSWORD = 'user' MONGO_DBNAME = ‘apitest'
  • 21.
    settings.py # let’s alsoadd some validation rules ! DOMAIN['people']['schema'] = { 'name': { 'type': 'string', 'maxlength': 50, 'unique': True} 'email': { 'type': 'string', 'regex': '^S+@S+$'}, 'location': { 'type': 'dict', 'schema': { 'address': {'type': 'string'}, 'city': {'type': 'string'}}}, 'born': {'type': ‘datetime'}}
  • 22.
    settings.py # let’s alsoadd some validation rules ! DOMAIN['people']['schema'] = { 'name': { 'type': 'string', 'maxlength': 50, 'unique': True} 'email': { 'type': 'string', 'regex': '^S+@S+$'}, 'location': { 'type': 'dict', 'schema': { 'address': {'type': 'string'}, 'city': {'type': 'string'}}}, 'born': {'type': ‘datetime'}} THIS REGEX SUCKS. DON’T USE IN PRODUCTION
  • 23.
    settings.py # allow writeaccess to API endpoints # (default is [‘GET’] for both settings) ! # /people RESOURCE_METHODS = ['GET','POST'] ! # /people/<id> ITEM_METHODS = ['GET','PATCH','PUT','DELETE']
  • 24.
    settings.py # allow writeaccess to API endpoints # (default is [‘GET’] for both settings) ! # /people RESOURCE_METHODS = ['GET','POST'] ! # /people/<id> ITEM_METHODS = ['GET','PATCH','PUT','DELETE'] ADD/CREATE ONE OR MORE ITEMS
  • 25.
    settings.py # allow writeaccess to API endpoints # (default is [‘GET’] for both settings) ! # /people RESOURCE_METHODS = ['GET', 'POST'] ! # /people/<id> ITEM_METHODS = ['GET','PATCH','PUT','DELETE'] EDIT ITEM
  • 26.
    settings.py # allow writeaccess to API endpoints # (default is [‘GET’] for both settings) ! # /people RESOURCE_METHODS = ['GET', 'POST'] ! # /people/<id> ITEM_METHODS = ['GET','PATCH','PUT','DELETE'] REPLACE ITEM
  • 27.
    settings.py # allow writeaccess to API endpoints # (default is [‘GET’] for both settings) ! # /people RESOURCE_METHODS = ['GET', 'POST'] ! # /people/<id> ITEM_METHODS = ['GET','PATCH','PUT','DELETE'] YOU GUESSED IT
  • 28.
    settings.py # a fewmore config options ! DOMAIN[‘people’].update( { ‘item_title’: ‘person’, ‘cache_control’: ‘max-age=10,must-revalidate, ‘cache_expires’: 10, ‘additional_lookup’: { ‘url’: ‘regex)”[w]+”)’, ‘field’: ‘name’ } )
  • 29.
  • 30.
  • 31.
  • 32.
    SORTING ?sort=[(“total”: -1)] SORT BY‘TOTAL’, DESCENDING ORDER
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
    NOT EMBEDDED $ curl-i <url> ! HTTP/1.1 200 OK {
 "title": "Book Title",
 "description": "book description",
 "author": “52da465a5610320002660f94"
 } RAW FOREIGN KEY (DEFAULT)
  • 38.
    EMBEDDED $ curl -i<url>?embedded={“author”: 1} ! HTTP/1.1 200 OK {
 "title": "Book Title",
 "description": "book description",
 "author": {
 “firstname”: “Mark”,
 “lastname”: “Green”,
 }
 } REQUEST EMBEDDED AUTHOR
  • 39.
    EMBEDDED $ curl -i<url>?embedded={“author”: 1} ! HTTP/1.1 200 OK {
 "title": "Book Title",
 "description": "book description",
 "author": {
 “firstname”: “Mark”,
 “lastname”: “Green”,
 }
 } EMBEDDED DOCUMENT
  • 40.
    JSON AND XML BUILT-INFOR ALL RESPONSES
  • 41.
    APPLICATION/JSON [ { "firstname": "Mark", "lastname": "Green", "born":"Sat, 23 Feb 1985 12:00:00 GMT", "role": ["copy", "author"], "location": {"city": "New York", "address": "4925 Lacross Road"}, "_id": "50bf198338345b1c604faf31", "_updated": "Wed, 05 Dec 2012 09:53:07 GMT", "_created": "Wed, 05 Dec 2012 09:53:07 GMT", "_etag": "ec5e8200b8fa0596afe9ca71a87f23e71ca30e2d", }, { "firstname": "John", ... }, ]
  • 42.
    APPLICATION/JSON [ { "firstname": "Mark", "lastname": "Green", "born":"Sat, 23 Feb 1985 12:00:00 GMT", "role": ["copy", "author"], "location": {"city": "New York", "address": "4925 Lacross Road"}, "_id": "50bf198338345b1c604faf31", "_updated": "Wed, 05 Dec 2012 09:53:07 GMT", "_created": "Wed, 05 Dec 2012 09:53:07 GMT", "_etag": "ec5e8200b8fa0596afe9ca71a87f23e71ca30e2d", }, { "firstname": "John", ... }, ] METAFIELDS ARE CONFIGURABLE
  • 43.
    APPLICATION/XML <resource href=“localhost:5000/people" title="people"> <resourcehref="localhost:5000/people/<id>" title="person"> <lastname>Green</lastname> <firstname>Mark</firstname> <born>Wed, 05 Dec 2012 09:53:07 GMT</born> <role>author</role> <role>copy</role> <location> <address>4925 Lacross Road</address> <city>New York</city> </location> <_id>50bf198338345b1c604faf31</_id> <created>Wed, 05 Dec 2012 09:53:07 GMT</created> <updated>Sat, 18 Jan 2014 09:16:10 GMT</updated> <etag>ec5e8200b8fa0596afe9ca71a87f23e71ca30e2d</etag> </resource> ... <resource>
  • 44.
    HATEOAS HYPERMEDIA AS THEENGINE OF APPLICATION STATE
  • 45.
    HATEOAS { “_links”: { “self”: { “href”:“/people”, “title”: “people”}, “parent”: { “href”: “/”, “title”: “home”}, “next”: { “href”: “/people?page=2”, “title”: “next page”}, “last”: { “href: “/people?page=10”, “title”: “last page”} } }
  • 46.
  • 47.
    FILE STORAGE FILES ARESTORED IN GRIDFS BY DEFAULT
  • 48.
    FILE STORAGE /SETTINGS accounts = {
 'name': {'type': 'string'},
 'pic': {'type': 'media'},
 …
 }
  • 49.
    FILE STORAGE $ curlF “name=doe” —F “pic=@profile.jpg" <url> HTTP/1.1 200 OK ! $ curl -i <url> HTTP/1.1 200 OK {
 "name": "john",
 "pic": "/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAA…"
 } MULTIPART/DATA-FORM POST
  • 50.
    FILE STORAGE $ curlF “name=doe” —F “pic=@profile.jpg" <url> HTTP/1.1 200 OK ! $ curl -i <url> HTTP/1.1 200 OK {
 "name": "john",
 "pic": "/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAA…"
 } FILES RETURNED AS BASE64 STRINGS
  • 51.
    FILE STORAGE (WITHMETA) $ curl -i <url> HTTP/1.1 200 OK {
 "name": "john",
 "pic": { “file”: ”/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAA”, “content_type”: “image/jpeg”, “name”: “profile.jpg”, “length”: 8129 }
 } EXTENDED_MEDIA_INFO: TRUE
  • 52.
  • 53.
    RATE LIMITING /SETTINGS # Rate limit on GET requests: # 1 requests 1 minute window (per client) ! RATE_LIMIT_GET = (1, 60)
  • 54.
    RATE LIMITING /GET #1 $ curl -i <url> ! HTTP/1.1 200 OK X-RateLimit-Limit: 1 X-RateLimit-Remaining: 0 X-RateLimit-Reset: 1390486659
  • 55.
    RATE LIMITING /GET #2 $ curl -i <url> ! HTTP/1.1 429 TOO MANY REQUESTS
  • 56.
    CONDITIONAL REQUESTS ALLOW CLIENTSTO ONLY REQUEST NON-CACHED CONTENT
  • 57.
    IF-MODIFIED-SINCE If-Modified-Since: Wed, 05Dec 2012 09:53:07 GMT “Please return modified data since <date> or 304”
  • 58.
  • 59.
    BULK INSERTS INSERT MULTIPLEDOCUMENTS WITH A SINGLE REQUEST
  • 60.
    BULK INSERTS /REQUEST $ curl -d ‘ [ { "firstname": "barack", "lastname": “obama" }, { "firstname": "mitt", "lastname": “romney” } ]' -H 'Content-Type: application/json’ <url>
  • 61.
    BULK INSERTS /RESPONSE [ { "_status": "OK", "_updated": "Thu, 22 Nov 2012 15:22:27 GMT", "_id": "50ae43339fa12500024def5b", "_etag": "749093d334ebd05cf7f2b7dbfb7868605578db2c" "_links": {"self": {"href": “<url>”, "title": "person"}} }, { "_status": "OK", "_updated": "Thu, 22 Nov 2012 15:22:27 GMT", "_id": "50ae43339fa12500024def5c", "_etag": "62d356f623c7d9dc864ffa5facc47dced4ba6907" "_links": {"self": {"href": “<url>", "title": "person"}} } ] COHERENCE MODE OFF: ONLY META FIELDS ARE RETURNED
  • 62.
    BULK INSERTS /RESPONSE [ { "_status": "OK", "_updated": "Thu, 22 Nov 2012 15:22:27 GMT", "_id": "50ae43339fa12500024def5b", "_etag": "749093d334ebd05cf7f2b7dbfb7868605578db2c" "_links": {"self": {"href": “<url>”, "title": “person”}}, "firstname": "barack", "lastname": "obama", ! }, { "_status": "OK", "_updated": "Thu, 22 Nov 2012 15:22:27 GMT", "_id": "50ae43339fa12500024def5c", "_etag": "62d356f623c7d9dc864ffa5facc47dced4ba6907" "_links": {"self": {"href": “<url>", "title": "person"}} "firstname": "mitt", "lastname": "romney", } ] COHERENCE MODE ON: ALL FIELDS RETURNED
  • 63.
    DATA INTEGRITY CONCURRENCY CONTROL NOOVERWRITING DOCUMENTS WITH OBSOLETE VERSIONS
  • 64.
    DATA INTEGRITY /CONCURRENCY $ curl -X PATCH -i <url> 
 -d '{"firstname": "ronald"}'
 HTTP/1.1 403 FORBIDDEN IF-MATCH MISSING
  • 65.
    DATA INTEGRITY /CONCURRENCY $ curl -X PATCH -i <url>
 -H "If-Match: <obsolete_etag>”
 -d '{"firstname": “ronald”}' ! HTTP/1.1 412 PRECONDITION FAILED
 ETAG MISMATCH
  • 66.
    DATA INTEGRITY /CONCURRENCY $ curl -X PATCH -i <url>
 -H “If-Match: 206fb4a39815cc0ebf48b2b52d7…”
 -d '{"firstname": “ronald"}' ! HTTP/1.1 200 OK UPDATE ALLOWED IF CLIENT AND SERVER ETAG MATCH
  • 67.
    DATA VALIDATION [ { "_status": "ERR", "_issues":{“name”: “value ‘clinton’ not unique”} }, { "_status": “OK", "_updated": "Thu, 22 Nov 2012 15:22:27 GMT”, "_id": “50ae43339fa12500024def5c", "_etag": “62d356f623c7d9dc864ffa5facc47dced4ba6907" "_links": { "self": { "href": “<url>”, "title": “person" } } } ]
  • 68.
  • 69.
    RUNS ON ALLPYTHONS 2.6 / 2.7 / 3.3 / 3.4 and PyPy
  • 70.
    AND MORE CORS. CACHECONTROL. VERSONING AND MORE.
  • 71.
  • 72.
  • 73.
    CUSTOM DATA LAYERS BUILDYOUR OWN DATA LAYER
  • 74.
  • 75.
    SQLALCHEMY (WIP) @registerSchema('invoices') class Invoices(CommonColumns): __tablename__= 'invoices' number = db.Column(db.Integer) people = db.Column(db.Integer, db.ForeignKey('people._id'))
  • 76.
  • 77.
  • 78.
  • 79.
    SECURITY AT AGLANCE • global authentication • custom endpoint auth • public enpoints and methods • role based access control • user restricted resource access
  • 80.
  • 81.
  • 82.
  • 83.
    #3 PASS CUSTOM CLASSTO THE EVE APP
  • 84.
  • 85.
    CUSTOM VALIDATION EXTEND THEBUILT-IN VALIDATION SYSTEM
  • 86.
    CUSTOM VALIDATION • addcustom data types • add custom validation logic
  • 87.
    EVENT HOOKS PLUG CUSTOMACTIONS INTO THE API LOOP
  • 88.
    EVENT HOOKS ATA GLANCE • POST on_insert/on_inserted • GET on_fetch/on_fetched • PATCH on_update/on_updated • PUT on_replace/on_replaced • DELETE on_delete/on_deteled • on_pre_<method>; on_post_<method>
  • 89.
  • 90.
    CUSTOM FILE STORAGE customMediaStorage subclasses to S3, File System, you name it
  • 91.
  • 92.
    EVE-DOCS GENERATES DOCUMENTATION FOREVE APIS IN HTML AND JSON FORMATS CHARLES FLYNN
  • 93.
  • 94.
    EVE-MONGOENGINE ENABLES MONGOENGINE ORMMODELS TO BE USED AS EVE SCHEMA STANISLAV HELLER
  • 95.
    EVE-ELASTIC ELASTICSEARCH DATA LAYERFOR EVE REST FRAMEWORK PETR JASEK
  • 96.
    EVE-MOCKER MOCKING TOOL FOREVE POWERED REST APIS THOMAS SILEO
  • 97.
    {48: <you namehere>} Bryan Cattle Christoph Witzany Daniele Pizzolli dccrazyboy Dong Wei Ming Florian Rathgeber Francisco Corrales Morales Garrin Kimmell Gianfranco Palumbo Jaroslav Semančík Jean Boussier John Deng Jorge Puente Sarrín Josh Villbrandt Julien Barbot Ken Carpenter Kevin Bowrin Kracekumar Nicolas Bazire Nicolas Carlier Ondrej Slinták Petr Jašek Paul Doucet Robert Wlodarczyk Roberto Pasini Ronan Delacroix Roy Smith Ryan Shea Samuel Sutch Stanislav Heller Thomas Sileo Tomasz Jezierski Xavi Cubillas
  • 98.
  • 99.
    GeoJSON Support and validationfor GeoJSON types ! Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon, GeometricalCollection
  • 100.
    JSONP* IN CASE YOURBROWSER/JS FRAMEWORK CANT HANDLE C.O.R.S. * PENDING SECURITY REVIEW
  • 101.
    JSON-LD / HAL/ SIREN* CURSTOM RENDER CLASSES * MAYBE (UNDER CONSIDERATION)
  • 102.
  • 103.