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. REST API FOR HUMANS eve REST WEB API eve

  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
  3. EVE IS POWERED BY

  4. None
  5. None
  6. None
  7. QUICKSTART

  8. RUN.PY from eve import Eve app = Eve() app.run()

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

    custom options and validation rules DOMAIN = { 'people': {} 'books': {} }
  10. LAUNCH THE API $ python run.py * Running on http://127.0.0.1:5000/

  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"} } }
  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
  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
  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
  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
  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'
  17. DEMO TIME!

  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
  19. SETTINGS.PY # enable writes. default is ['GET'] # /people RESOURCE_METHODS

    = ['GET','POST'] # /people/<id> ITEM_METHODS = ['GET','PATCH','PUT','DELETE']
  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
  21. # enable writes. default is ['GET'] # /people RESOURCE_METHODS =

    ['GET','POST'] # /people/<id> ITEM_METHODS = ['GET','PATCH','PUT','DELETE'] SETTINGS.PY UPDATE DOCUMENT
  22. # enable writes. default is ['GET'] # /people RESOURCE_METHODS =

    ['GET','POST'] # /people/<id> ITEM_METHODS = ['GET','PATCH','PUT','DELETE'] SETTINGS.PY REPLACE DOCUMENT
  23. # enable writes. default is ['GET'] # /people RESOURCE_METHODS =

    ['GET','POST'] # /people/<id> ITEM_METHODS = ['GET','PATCH','PUT','DELETE'] SETTINGS.PY YOU GUESSED IT
  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' } )
  25. FEATURES

  26. MONGO FILTERS ?where={“lastname”: “Doe”}

  27. PYTHON FILTERS ?where=lastname==“Doe”

  28. SORTING ?sort=-total SORT BY ‘TOTAL’, DESCENDING DEMO

  29. SORTING ?sort=[(“total”: -1)] SAME, MONGODB-STYLE

  30. PAGINATION ?max_results=20&page=2 MAX 20 RESULTS/PAGE; PAGE 2

  31. PROJECTIONS ?projection={"avatar": 0} RETURN ALL FIELDS BUT ‘AVATAR’

  32. PROJECTIONS ?projection={"lastname": 1} ONLY RETURN ‘LASTNAME’

  33. EMBEDDED RESOURCES ?embedded={"author": 1} DEMO

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

    "title": "Book Title", "description": "book description", "author": "52da465a5610320002660f94" } RAW FOREIGN KEY
  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
  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
  37. JSON AND XML SERIALIZATION FOR ALL RESPONSES DEMO

  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", ... }, ]
  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
  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>
  41. HATEOAS HYPERMEDIA AS THE ENGINE OF APPLICATION STATE

  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"} } }
  43. DOCUMENT VERSIONS ?version=3 ?version=all ?version=diffs

  44. FILE STORAGE FILES ARE STORED IN GRIDFS BY DEFAULT

  45. FILE STORAGE / SETTINGS accounts = { "name": {"type": "string"},

    "pic": {"type": "media"}, }
  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
  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
  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’]
  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"
  50. RATE LIMITING POWERED

  51. RATE LIMITING / SETTINGS # Set rate limit on GET

    requests: # 1 requests 1 minute window (per client) RATE_LIMIT_GET = (1, 60)
  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
  53. RATE LIMITING / SECOND REQUEST $ curl -i <url> HTTP/1.1

    429 TOO MANY REQUESTS
  54. CONDITIONAL REQUESTS ALLOW CLIENTS TO ONLY REQUEST NON-CACHED CONTENT DEMO

  55. IF-MODIFIED-SINCE If-Modified-Since: Wed, 05 Dec 2012 09:53:07 GMT “Return document

    is changed since <date>, or 304”
  56. IF-NONE-MATCH If-None-Match:1234567890123456789012345678901234567890 “Return data if it has changed (ETAG differs

    from mine), or 304” >
  57. BULK INSERTS INSERT MULTIPLE DOCUMENTS WITH A SINGLE REQUEST DEMO

  58. BULK INSERTS / REQUEST $ curl -d ' [ {

    "firstname": "barack", "lastname": "obama" }, { "firstname": "mitt", "lastname": "romney" } ]' -H 'Content-Type: application/json' <url>
  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
  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
  61. DATA INTEGRITY AND CONCURRENCY CONTROL NO OVERWRITING OF ANY DOCUMENT

    WITH OBSOLETE VERSIONS DEMO
  62. DATA INTEGRITY AND CONSISTENCY $ curl -X PATCH -i <url>

    -d '{"firstname": "ronald"}' HTTP/1.1 428 PRECONDITION REQUIRED IF-MATCH MISSING
  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
  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
  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
  66. GEOJSON SUPPORT AND VALIDATION FOR GEOJSON TYPES POINT, LINE-STRING, POLYGON,

    MULTI-POINT, MULTILINE-STRING, MULTI-POLYGON, GEOMETRICAL COLLECTION
  67. AUTHENTICATION AND AUTHORIZATION BASIC, TOKEN AND HMAC AUTH SUPPORTED

  68. RUNS ON ALL PYTHONS 2.7 / 3.4+ / PYPY 3

  69. AND MUCH MORE CORS / CACHE CONTROL / OPLOG /

    SOFT DELETES LOGGING / CUSTOM ID FIELDS / JSONP / MULTI-DB / AGGREGATION / ETC.
  70. BSD LICENSED USE IN BOTH OPEN SOURCE AND COMMERCIAL

  71. DEVELOPERS

  72. CUSTOM DATA LAYERS BUILD YOUR OWN DATA LAYER

  73. SQL ALCHEMY

  74. ELASTICSERCH

  75. AUTHENTICATION BASIC | TOKEN | HMAC DEMO

  76. SECURITY AT A GLANCE • global authentication • endpoint authentication

    • public enpoints and methods • role based access control • user restricted resource access
  77. THREE STEPS AUTH TUTORIAL

  78. ONE. IMPORT BASE AUTH CLASS

  79. TWO. OVERRIDE CHECK_AUTH() METHOD

  80. THREE. PASS CUSTOM CLASS TO THE EVE APP

  81. CUSTOM VALIDATION EXTEND THE BUILT-IN VALIDATION SYSTEM

  82. CUSTOM VALIDATION • add custom data types • add custom

    validation logic • normalization
  83. EVENT HOOKS PLUG CUSTOM ACTIONS INTO THE API LOOP DEMO

  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>
  85. TRANSFORM INCOMING DOCUMENTS

  86. EVE IS FLASK ‘CLASSIC’ FLASK POWER IS AT YOUR FINGERTIPS

    DEMO
  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()
  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()
  89. CUSTOM FILE STORAGE CUSTOM STORAGE CLASSES: S3 / FILE SYSTEM

    / ETC.
  90. COMMUNITY

  91. EVE-SWAGGER SWAGGER FOR EVE

  92. EVE-SWAGGER

  93. EVE-SQLALCHEMY SQL DATA LAYER FOR EVE

  94. EVE-ELASTIC ELASTICSEARCH DATA LAYER FOR EVE REST FRAMEWORK

  95. EVE.NET CROSS PLATFORM ASYNC C# CLIENT FOR .NET APPS PETR

    JASEK
  96. EVE-AUTH-JWT OAUTH 2.0 JWT VALIDATION AUTHENTICATION THOMAS SILEO

  97. FLASK-SENTINEL OAUTH2 SERVER THOMAS SILEO

  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
  99. PYTHON-EVE.ORG eve REST WEB API eve @nicolaiarocci / nicolaiarocci.com /

    eve@nicolaiarocci.com thanks!
  100. None