Eve - REST API for Humans™

Eve - REST API for Humans™

Introducing the Eve REST API Framework.

PyCon Web 2018, Munich
PyCon Belarus 2018, Minsk
PiterPy 2016, St. Petersburg
EuroPython 2014, Berlin
Python Meetup, Helsinki
PyCon Italy 2014, Florence
PyCon Sweden 2014, Stockholm
FOSDEM 2014, Brussels

E3550767c858c787c35c280047ff789c?s=128

Nicola Iarocci

February 02, 2014
Tweet

Transcript

  1. 2.

    NICOLA IAROCCI Co-Founder @ CIR2000 Lead Developer @ gestionaleamica.com Microsoft

    MVP MongoDB Master Open Source Author CoderDojo DevRomagna @nicolaiarocci / nicolaiarocci.com / eve@nicolaiarocci.com
  2. 4.
  3. 5.
  4. 6.
  5. 9.

    SETTINGS.PY # just a couple API endpoints with no #

    custom options and validation rules DOMAIN = { 'people': {} 'books': {} }
  6. 11.

    TEST YOUR API $ curl -i http://localhost:5000/ { "_items": [],

    "_links": { "self": { "href": "127.0.0.1:5000/people", "title": "people" }, "parent": { "href": "127.0.0.1:5000", "title": "home"} } }
  7. 12.

    $ curl -i http://localhost:5000/ { "_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 TEST YOUR API
  8. 13.

    $ curl -i http://localhost:5000/ { "_items": [], "_links": { "self":

    { "href": "127.0.0.1:5000/people", "title": "people" }, "parent": { "href": "127.0.0.1:5000", "title": "home"} } } TEST YOUR API CLIENTS CAN EXPLORE THE API PROGRAMMATICALLY
  9. 14.

    $ curl -i http://localhost:5000/ { "_items": [], "_links": { "self":

    { "href": "127.0.0.1:5000/people", "title": "people" }, "parent": { "href": "127.0.0.1:5000", "title": "home"} } } TEST YOUR API AND EVENTUALLY FILL THEIR UI
  10. 15.

    $ curl -i http://localhost:5000/ { "_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 TEST YOUR API
  11. 16.

    SETTINGS.PY # connect to mongo MONGO_HOST = 'localhost' MONGO_PORT =

    27017 MONGO_USERNAME = 'user' MONGO_PASSWORD = 'pw' MONGO_DBNAME = 'apitest' # or (better): MONGO_URI = ‘mongodb://user:pw@localhost:27017/apitest'
  12. 18.

    # add fields and validation rules for 'people' endpoint 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'}} USE BETTER REGEX IN PRODUCTION SETTINGS.PY
  13. 19.

    SETTINGS.PY # enable writes. default is ['GET'] # /people RESOURCE_METHODS

    = ['GET','POST'] # /people/<id> ITEM_METHODS = ['GET','PATCH','PUT','DELETE']
  14. 20.

    # enable writes. default is ['GET'] # /people RESOURCE_METHODS =

    ['GET','POST'] # /people/<id> ITEM_METHODS = ['GET','PATCH','PUT','DELETE'] SETTINGS.PY ADD/CREATE ONE OR MORE ITEMS
  15. 21.

    # enable writes. default is ['GET'] # /people RESOURCE_METHODS =

    ['GET','POST'] # /people/<id> ITEM_METHODS = ['GET','PATCH','PUT','DELETE'] SETTINGS.PY UPDATE DOCUMENT
  16. 22.

    # enable writes. default is ['GET'] # /people RESOURCE_METHODS =

    ['GET','POST'] # /people/<id> ITEM_METHODS = ['GET','PATCH','PUT','DELETE'] SETTINGS.PY REPLACE DOCUMENT
  17. 23.

    # enable writes. default is ['GET'] # /people RESOURCE_METHODS =

    ['GET','POST'] # /people/<id> ITEM_METHODS = ['GET','PATCH','PUT','DELETE'] SETTINGS.PY YOU GUESSED IT
  18. 24.

    SETTINGS.PY # a few additional configuration options DOMAIN['people'].update( { 'item_title':

    'person', 'cache_control': 'max-age=10,must-revalidate', 'cache_expires': 10, 'additional_lookup': { 'url': 'regex("[\w]+")', 'field': 'name' } )
  19. 25.
  20. 34.

    NOT EMBEDDED $ curl -i <url> HTTP/1.1 200 OK {

    "title": "Book Title", "description": "book description", "author": "52da465a5610320002660f94" } RAW FOREIGN KEY
  21. 35.

    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
  22. 36.

    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
  23. 38.

    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", ... }, ]
  24. 39.

    [ { "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 METAFIELDS ARE CUSTOMIZABLE
  25. 40.

    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>
  26. 42.

    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"} } }
  27. 46.

    FILE UPLOAD $ 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
  28. 47.

    FILE UPLOAD $ 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
  29. 48.

    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: [‘content_type, ‘name’, ‘length’]
  30. 49.

    FILE STORAGE (DEDICATED ENDPOINT) $ curl -i <url> HTTP/1.1 200

    OK { "name": "john", "pic": "/media/profile.jpg" } } RETURN_MEDIA_AS_URL = True MEDIA_ENDPOINT = "media"
  31. 51.

    RATE LIMITING / SETTINGS # Set rate limit on GET

    requests: # 1 requests 1 minute window (per client) RATE_LIMIT_GET = (1, 60)
  32. 52.

    RATE LIMITING / FIRST REQUEST $ curl -i <url> HTTP/1.1

    200 OK X-RateLimit-Limit: 1 X-RateLimit-Remaining: 0 X-RateLimit-Reset: 1390486659
  33. 58.

    BULK INSERTS / REQUEST $ curl -d ' [ {

    "firstname": "barack", "lastname": "obama" }, { "firstname": "mitt", "lastname": "romney" } ]' -H 'Content-Type: application/json' <url>
  34. 59.

    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
  35. 60.

    BULK INSERTS / RESPONSE [ { "_status": "OK", "_updated": "Thu,

    22 Nov 2012 15:22:27 GMT", "_id": "50ae43339fa12500024def5b", "_etag": "749093d334ebd05cf7f2b7dbfb7868605578db2c" "_links": {"self": {"href": "<url>", "title": }}, "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
  36. 62.

    DATA INTEGRITY AND CONSISTENCY $ curl -X PATCH -i <url>

    -d '{"firstname": "ronald"}' HTTP/1.1 428 PRECONDITION REQUIRED IF-MATCH MISSING
  37. 63.

    $ curl -X PATCH -i <url> -H "If-Match: <obsolete_etag>" -d

    '{"firstname": "ronald"}' HTTP/1.1 412 PRECONDITION FAILED ETAG MISMATCH DATA INTEGRITY AND CONSISTENCY
  38. 64.

    $ curl -X PATCH -i <url> -H "If-Match: 206fb4a39815cc0ebf48b2b52d7…” -d

    '{"firstname": "ronald"}' HTTP/1.1 200 OK UPDATE ALLOWED AS CLIENT AND SERVER ETAG ARE MATCHING DATA INTEGRITY AND CONSISTENCY
  39. 65.

    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" } } } ] powered by Cerberus python-cerberus.org
  40. 66.

    GEOJSON SUPPORT AND VALIDATION FOR GEOJSON TYPES POINT, LINE-STRING, POLYGON,

    MULTI-POINT, MULTILINE-STRING, MULTI-POLYGON, GEOMETRICAL COLLECTION
  41. 69.

    AND MUCH MORE CORS / CACHE CONTROL / OPLOG /

    SOFT DELETES LOGGING / CUSTOM ID FIELDS / JSONP / MULTI-DB / AGGREGATION / ETC.
  42. 76.

    SECURITY AT A GLANCE • global authentication • endpoint authentication

    • public enpoints and methods • role based access control • user restricted resource access
  43. 82.
  44. 84.

    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>
  45. 87.

    FLASK AT YOUR FINGERTIPS from eve import Eve app =

    Eve() # add a regular Flask endpoint @app.route('/hello') def hello_world(): return 'Hello World!' if __name__ == '__main__': app.run()
  46. 88.

    FLASK AT YOUR FINGERTIPS from eve import Eve from eve.auth

    import requires_auth app = Eve() # add Eve auth to Flask endpoint @app.route('/hello') @requires_auth('resource') def hello_world(): return 'Hello World!' if __name__ == '__main__': app.run()
  47. 90.
  48. 98.

    {137: <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
  49. 100.