SlideShare a Scribd company logo
1 of 55
Download to read offline
REST APIREST API
USING FLASK & SQLALCHEMYUSING FLASK & SQLALCHEMY
Alessandro Cucci
Python Developer, Energee3
REPRESENTATIONAL STATE TRANSFERREPRESENTATIONAL STATE TRANSFER
ROY THOMAS FIELDING - 2010ROY THOMAS FIELDING - 2010
HTTP://WWW.ICS.UCI.EDU/~FIELDING/PUBS/DISSERTATION/REST_ARCH_STYLE.HTMHTTP://WWW.ICS.UCI.EDU/~FIELDING/PUBS/DISSERTATION/REST_ARCH_STYLE.HTM
RESTREST
GUIDING CONSTRAINTSGUIDING CONSTRAINTS
CLIENT-SERVERCLIENT-SERVER
STATELESSSTATELESS
CACHEABLECACHEABLE
LAYERED SYSTEMLAYERED SYSTEM
CODE ON DEMAND (OPTIONAL)CODE ON DEMAND (OPTIONAL)
RESTREST
GUIDING CONSTRAINTSGUIDING CONSTRAINTS
UNIFORM INTERFACEUNIFORM INTERFACE
IDENTIFICATION OF RESOURCESIDENTIFICATION OF RESOURCES
MANIPULATION OF RESOURCES THROUGH REPRESENTATIONSMANIPULATION OF RESOURCES THROUGH REPRESENTATIONS
SELF-DESCRIPTIVE MESSAGESSELF-DESCRIPTIVE MESSAGES
HYPERMEDIA AS THE ENGINE OF APPLICATION STATEHYPERMEDIA AS THE ENGINE OF APPLICATION STATE
REST URI EXAMPLESREST URI EXAMPLES
h�p://myapi.com/customers
h�p://myapi.com/customers/33245
REST ANTI-PATTERNSREST ANTI-PATTERNS
h�p://myapi.com/update_customer&id=12345&
format=json
h�p://myapi.com/customers/12345/update
RELATIONSHIP BETWEEN URL AND HTTPRELATIONSHIP BETWEEN URL AND HTTP
METHODSMETHODS
URL GET PUT POST DELETE
h�p://api.myvinylcollec�on.com
/records/
LIST of records in
collec�on
Method not
allowed.
CREATE a
new entry in
the
collec�on.
DELETE the
en�re
collec�on.
h�p://api.myvinylcollec�on.com
/records/1
RETRIEVE a
representa�on of
the addressed
member of the
collec�on
REPLACE
the
addressed
member of
the
collec�on.
Method not
allowed.
DELETE the
addressed
member of
the
collec�on.
WHAT DO WE NOT CARE FOR THIS EVENINGWHAT DO WE NOT CARE FOR THIS EVENING
Stability & Tes�ng
Long-Term maintainability
Edge Cases
Opera�ons, Caching & Deployment
MY VINYL COLLECTION APIMY VINYL COLLECTION API
HTTP://FLASK.POCOO.ORG/HTTP://FLASK.POCOO.ORG/
HELLO API!HELLO API!
from flask import Flask
app = Flask(__name__)
if __name__ == '__main__':
app.run()
$ python myvinylcollectionapi.py
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
HELLO API!HELLO API!
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello PyRE!"
if __name__ == '__main__':
app.run()
HELLO API!HELLO API!
from flask import Flask, jsonify
app = Flask(__name__)
@app.route("/")
def hello():
return jsonify(data="Hello PyRE!")
if __name__ == '__main__':
app.run()
HTTP GET METHODSHTTP GET METHODS
URL GET
h�p://api.myvinylcollec�on.com
/records
LIST of records in
collec�on
h�p://api.myvinylcollec�on.com
/records/1
RETRIEVE a
representa�on of the
addressed member of
the collec�on
from flask import Flask, jsonify, abort
app = Flask(__name__)
RECORDS = [
{
'id': 0,
'artist': "Queen",
'title': "A Night At The Opera",
'year': "1975",
'label': "EMI"
},
{
'id': 1,
'artist': "Pink Floyd",
'title': "The Dark Side Of The Moon",
'year': "1989",
'label': "EMI"
},
...
]
@app.route("/records")
def get_records():
return jsonify(RECORDS)
@app.route("/records/<int:index>")
def get_record(index):
try:
record = RECORDS[index]
except IndexError:
abort(404)
return jsonify(record)
if __name__ == '__main__':
app.run()
HTTP GET METHODHTTP GET METHOD
$ curl -X GET localhost:5000/records
[
{
"artist": "Queen",
"id": 0,
"label": "EMI",
"title": "A Night At The Opera",
"year": "1975"
},
{
"artist": "Pink Floyd",
"id": 1,
"label": "EMI",
"title": "The Dark Side Of The Moon",
"year": "1989"
}
]
HTTP GET Method
$ curl -X GET localhost:5000/records/1
{
"artist": "Pink Floyd",
"id": 1,
"label": "EMI",
"title": "The Dark Side Of The Moon",
"year": "1989"
}
$ curl -X GET localhost:5000/records/5
<title>404 Not Found</title>
<h1>Not Found</h1>
<p>The requested URL was not found on the server.
If you entered the URL manually please check
your spelling and try again.</p>
JSONIFY THAT ERROR!JSONIFY THAT ERROR!
@app.errorhandler(404)
def page_not_found(error):
return jsonify(
error="Not Found",
status_code=404
), 404
$ curl -X GET localhost:5000/records/5
{
"error": "Not Found",
"status_code": 404
}
GOOD NEWS:GOOD NEWS:
IT WORKS!IT WORKS!
BAD NEWS:BAD NEWS:
STATIC DATASTATIC DATA
SEPARATION OF CONCERNSSEPARATION OF CONCERNS
SQLITESQLITE + DISCOGS.COM+ DISCOGS.COM + PANDAS+ PANDAS
CREATE TABLE "collection" (
`index` INTEGER PRIMARY KEY AUTOINCREMENT,
`Catalog#` TEXT,
`Artist` TEXT,
`Title` TEXT,
`Label` TEXT,
`Format` TEXT,
`Rating` REAL,
`Released` INTEGER,
`release_id` INTEGER,
`CollectionFolder` TEXT,
`Date Added` TEXT,
`Collection Media Condition` TEXT,
`Collection Sleeve Condition` TEXT,
`Collection Notes` REAL
)
CSV COLLECTION EXPORTCSV COLLECTION EXPORT
import pandas
import sqlite3
conn = sqlite3.connect('record_collection.db')
conn.text_factory = sqlite3.Binary
df = pandas.read_csv('collection.csv')
df.to_sql('collection', conn)
OBJECT RELATIONAL MAPPER (ORM)OBJECT RELATIONAL MAPPER (ORM)
HTTP://DOCS.SQLALCHEMY.ORGHTTP://DOCS.SQLALCHEMY.ORG
MODEL.PYMODEL.PY
from flask_sqlalchemy import SQLAlchemy
from flask_sqlalchemy import orm
db = SQLAlchemy()
class Record(db.Model):
__tablename__ = "collection"
index = db.Column(db.Integer, primary_key=True)
Artist = db.Column(db.Text, nullable=False)
Title = db.Column(db.Text, nullable=False)
Label = db.Column(db.Text)
Released = db.Column(db.Text)
def as_dict(self):
columns = orm.class_mapper(self.__class__).mapped_table.c
return {
col.name: getattr(self, col.name)
for col in columns
}
QUERYQUERY
>>> # .all() return a list
>>> all_records = Record.query.all()
>>> len(all_records)
80
>>> # .first() return the first item that matches
>>> record = Record.query.filter(Record.index == 9).first()
>>> record.Title
"Back In Black"
>>> record.Artist
"AC/DC"
>>> record.Released
"1980"
>>> # .filter_by() is a shortcut
>>> record = Record.query.filter_by(index == 6).first()
>>> record.Title
"Hotel California"
from flask import Flask, jsonify
from model import db, Record
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///record_collection.db"
db.init_app(app)
@app.route("/records")
def get_records():
records = [r.as_dict() for r in Record.query.all()]
return jsonify(records)
@app.route("/records/<int:index>")
def get_record(index):
record = Record.query.filter(Record.index == index).first_or_404()
return jsonify(record.as_dict())
$ curl -X GET localhost:5000/records
[
{
"Artist": "The Police",
"index": 0,
"Title": "Reggatta De Blanc"
},
{
"Artist": "The Beatles",
"index": 1,
"Title": "Abbey Road"
},
...
]
$ curl -X GET localhost:5000/records/1
{
"Artist": "The Beatles",
"index": 1,
"Title": "Abbey Road"
}
HTTP POST METHODSHTTP POST METHODS
URL POST
h�p://api.myvinylcollec�on.com
/records/
CREATE a new
entry in the
collec�on.
h�p://api.myvinylcollec�on.com
/records/1
Method not
allowed.
POST ON LOCALHOST:5000/RECORDS/IDPOST ON LOCALHOST:5000/RECORDS/ID
@app.errorhandler(405)
def method_not_allowed(error):
return jsonify(
error="Method Not Allowed",
status_code=405
), 405
from flask import Flask, jsonify, abort, request
...
@app.route("/records/<int:index>", methods=['GET', 'POST'])
def get_record(index):
if request.method == 'POST':
abort(405)
record = Record.query.filter(Record.index == index).first_or_404()
return jsonify(record.as_dict())
$ curl -X POST localhost:5000/records/1
{
"error": "Method Not Allowed",
"status_code": 405
}
INSERT INTO DATABASEINSERT INTO DATABASE
>>> # .add() insert a record
>>> db.session.add(record)
>>> # changes won't be saved until committed!
>>> db.session.commit()
ADDING A RECORD TO MY COLLECTIONADDING A RECORD TO MY COLLECTION
@app.route("/records", methods=['GET', 'POST'])
def get_records():
if request.method == 'POST':
record = Record(**json.loads(request.data))
db.session.add(record)
db.session.commit()
return jsonify(record.as_dict()), 201
records = [r.as_dict() for r in Record.query.all()]
return jsonify(records)
ADDING A RECORD TO MY COLLECTIONADDING A RECORD TO MY COLLECTION
$ curl -i -H "Content-Type: application/json" -X POST localhost:5000/records 
> -d '{"Artist":"Neil Joung", "Title":"Harvest", 
> "Label":"Reprise Records", "Released":"1977"}'
HTTP/1.0 201 CREATED
Content-Type: application/json
Content-Length: 104
Server: Werkzeug/0.11.11 Python/2.7.12+
Date: Sat, 03 Dec 2016 11:03:10 GMT
{
"Artist": "Neil Young",
"Label": "Reprise Records",
"Released": "1977",
"Title": "American Stars 'N Bars",
"index": 91
}
HTTP PUT METHODSHTTP PUT METHODS
URL PUT
h�p://api.myvinylcollec�on.com
/records/
Method not allowed.
h�p://api.myvinylcollec�on.com
/records/1
REPLACE the
addressed member
of the collec�on.
@app.route("/records", methods=['GET', 'POST', 'PUT'])
def get_records():
if request.method == 'POST':
record = Record(**json.loads(request.data))
db.session.add(record)
db.session.commit()
return jsonify(record.as_dict()), 201
elif request.method == 'PUT':
abort(405)
records = [r.as_dict() for r in Record.query.all()]
return jsonify(records), 200
@app.route("/records/<int:index>", methods=['GET', 'POST', 'PUT'])
def get_record(index):
if request.method == 'POST':
abort(405)
else:
record = Record.query.filter(Record.index == index).first_or_404()
if request.method == 'PUT':
for k, v in json.loads(request.data).iteritems():
setattr(record, k, v)
db.session.add(record)
db.session.commit()
return jsonify(record.as_dict()), 200
PUT ON COLLECTIONPUT ON COLLECTION
$ curl -i -H "Content-Type: application/json" 
> -X POST localhost:5000/records 
> -d '{"Artist":"Neil Joung", "Title":"Harvest", 
> "Label":"Reprise Records", "Released":"1977"}'
HTTP/1.0 405 METHOD NOT ALLOWED
Content-Type: application/json
Content-Length: 59
Server: Werkzeug/0.11.11 Python/2.7.12+
Date: Sat, 03 Dec 2016 10:20:06 GMT
{
"error": "Method Not Allowed",
"status_code": 405
}
PUT ON RESOURCEPUT ON RESOURCE
$ curl -i -H "Content-Type: application/json" 
> -X PUT localhost:5000/records/91 
> -d '{"Artist":"Neil Joung", "Title":"Harvest", 
> "Label":"Reprise Records", "Released":"1977"}'
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 104
Server: Werkzeug/0.11.11 Python/2.7.12+
Date: Sat, 03 Dec 2016 11:07:22 GMT
{
"Artist": "Neil Young",
"Label": "Reprise Records",
"Released": "1977",
"Title": "American Stars 'N Bars",
"index": 91
}
HTTP DELETE METHODSHTTP DELETE METHODS
URL DELETE
h�p://api.myvinylcollec�on.com
/records/
DELETE the en�re
collec�on.
h�p://api.myvinylcollec�on.com
/records/1
DELETE the
addressed member
of the collec�on.
DELETE ON COLLECTIONDELETE ON COLLECTION
@app.route("/records", methods=['GET', 'POST', 'PUT', 'DELETE'])
def get_records():
if request.method == 'POST':
record = Record(**json.loads(request.data))
db.session.add(record)
db.session.commit()
return jsonify(record.as_dict()), 201
elif request.method == 'PUT':
abort(405)
records = [r.as_dict() for r in Record.query.all()]
if request.method == 'DELETE':
for r in records:
db.session.delete(r)
db.session.commit()
records = [r.as_dict() for r in Record.query.all()]
return jsonify(records), 200
DELETE ON RESOURCEDELETE ON RESOURCE
@app.route("/records/<int:index>", methods=['GET', 'POST', 'PUT', 'DELETE'])
def get_record(index):
if request.method == 'POST':
abort(405)
else:
record = Record.query.filter(Record.index == index).first_or_404()
if request.method == 'PUT':
for k, v in json.loads(request.data).iteritems():
setattr(record, k, v)
db.session.add(record)
db.session.commit()
elif request.method == 'DELETE':
db.session.delete(record)
db.session.commit()
return jsonify(record.as_dict()), 200
DELETE ON RESOURCEDELETE ON RESOURCE
$ curl -i -X DELETE localhost:5000/records/91
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 104
Server: Werkzeug/0.11.11 Python/2.7.12+
Date: Sat, 03 Dec 2016 10:40:00 GMT
{
"Artist": "Neil Young",
"Label": "Reprise Records",
"Released": "1977",
"Title": "American Stars 'N Bars",
"index": 91
}
DELETE ON RESOURCEDELETE ON RESOURCE
$ curl -i -X DELETE localhost:5000/records/91
HTTP/1.0 HTTP/1.0 404 NOT FOUND
Content-Type: application/json
Content-Length: 50
Server: Werkzeug/0.11.11 Python/2.7.12+
Date: Sat, 03 Dec 2016 10:40:09 GMT
{
"error": "Not Found",
"status_code": 404
}
DELETE ON COLLECTIONDELETE ON COLLECTION
$ curl -i -X DELETE localhost:5000/records
FLASK-LOGINFLASK-LOGIN
HTTPS://FLASK-LOGIN.READTHEDOCS.IOHTTPS://FLASK-LOGIN.READTHEDOCS.IO
PWD AUTHENTICATIONPWD AUTHENTICATION
from flask import Flask, jsonify, abort
from flask_login import LoginManager, current_user
app = Flask(__name__)
login_manager = LoginManager(app)
@login_manager.request_loader
def check_token(request):
token = request.headers.get('Authorization')
if token == 'L3T_M3_PA55!':
return "You_can_pass" # DON'T TRY THIS AT HOME!
return None
@app.route("/")
def get_main_root():
if current_user:
return jsonify(data='Hello Login'), 200
else:
abort(401)
HOW IT WORKSHOW IT WORKS
$ curl -i localhost:5000
HTTP/1.0 401 UNAUTHORIZED
Content-Type: application/json
WWW-Authenticate: Basic realm="Authentication Required"
Content-Length: 37
Server: Werkzeug/0.11.11 Python/2.7.12+
Date: Sat, 03 Dec 2016 14:46:55 GMT
{
"error": "Unauthorized access"
}
$ curl -i -H "Authorization: L3T_M3_PA55!" localhost:5000
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 28
Server: Werkzeug/0.11.11 Python/2.7.12+
Date: Sat, 03 Dec 2016 14:42:00 GMT
{
"data": "Hello Login"
}
SECURING OUR API - RESOURCESECURING OUR API - RESOURCE
@app.route("/records/<int:index>", methods=['GET', 'POST', 'PUT', 'DELETE'])
def get_record(index):
if request.method == 'POST':
abort(405)
else:
record = Record.query.filter(Record.index == index).first_or_404()
if request.method == 'PUT':
if current_user:
for k, v in json.loads(request.data).iteritems():
setattr(record, k, v)
db.session.add(record)
db.session.commit()
else:
abort(401)
elif request.method == 'DELETE':
if current_user:
db.session.delete(record)
db.session.commit()
else:
abort(401)
return jsonify(record.as_dict()), 200
SECURING OUR API - COLLECTIONSECURING OUR API - COLLECTION
@app.route("/records", methods=['GET', 'POST', 'PUT', 'DELETE'])
def get_records():
if request.method == 'POST':
record = Record(**json.loads(request.data))
db.session.add(record)
db.session.commit()
return jsonify(record.as_dict()), 201
elif request.method == 'PUT':
abort(405)
records = [r.as_dict() for r in Record.query.all()]
if request.method == 'DELETE':
if current_user:
for r in records:
db.session.delete(r)
db.session.commit()
records = [r.as_dict() for r in Record.query.all()]
return jsonify(records), 200
else:
abort(401)
return jsonify(records), 200
HOMEWORKSHOMEWORKS
Pagina�on with Flask-SqlAlchemy
Rate Limi�ng with Flask-Limiter
Cache with Flask-Cache
THANK YOU!THANK YOU!
{
'slides': 'www.alessandrocucci.it/pyre/restapi',
'code': 'https://goo.gl/4UOqEr'
}

More Related Content

What's hot

Spring bootでweb 基本編
Spring bootでweb 基本編Spring bootでweb 基本編
Spring bootでweb 基本編なべ
 
Elasticsearch Query DSL - Not just for wizards...
Elasticsearch Query DSL - Not just for wizards...Elasticsearch Query DSL - Not just for wizards...
Elasticsearch Query DSL - Not just for wizards...clintongormley
 
Reactive design: languages, and paradigms
Reactive design: languages, and paradigmsReactive design: languages, and paradigms
Reactive design: languages, and paradigmsDean Wampler
 
Spring Web Service, Spring Integration and Spring Batch
Spring Web Service, Spring Integration and Spring BatchSpring Web Service, Spring Integration and Spring Batch
Spring Web Service, Spring Integration and Spring BatchEberhard Wolff
 
Capacity Planning For Your Growing MongoDB Cluster
Capacity Planning For Your Growing MongoDB ClusterCapacity Planning For Your Growing MongoDB Cluster
Capacity Planning For Your Growing MongoDB ClusterMongoDB
 
PostgreSQL and JDBC: striving for high performance
PostgreSQL and JDBC: striving for high performancePostgreSQL and JDBC: striving for high performance
PostgreSQL and JDBC: striving for high performanceVladimir Sitnikov
 
Fess/Elasticsearchを使った業務で使える?全文検索への道
Fess/Elasticsearchを使った業務で使える?全文検索への道Fess/Elasticsearchを使った業務で使える?全文検索への道
Fess/Elasticsearchを使った業務で使える?全文検索への道Shinsuke Sugaya
 
Micrometer/Prometheusによる大規模システムモニタリング #jsug #sf_26
Micrometer/Prometheusによる大規模システムモニタリング #jsug #sf_26Micrometer/Prometheusによる大規模システムモニタリング #jsug #sf_26
Micrometer/Prometheusによる大規模システムモニタリング #jsug #sf_26Yahoo!デベロッパーネットワーク
 
Planet-scale Data Ingestion Pipeline: Bigdam
Planet-scale Data Ingestion Pipeline: BigdamPlanet-scale Data Ingestion Pipeline: Bigdam
Planet-scale Data Ingestion Pipeline: BigdamSATOSHI TAGOMORI
 
Developing RESTful Web APIs with Python, Flask and MongoDB
Developing RESTful Web APIs with Python, Flask and MongoDBDeveloping RESTful Web APIs with Python, Flask and MongoDB
Developing RESTful Web APIs with Python, Flask and MongoDBNicola Iarocci
 
My sql 5.6 master slave and master-master replication.step by step configurat...
My sql 5.6 master slave and master-master replication.step by step configurat...My sql 5.6 master slave and master-master replication.step by step configurat...
My sql 5.6 master slave and master-master replication.step by step configurat...Pawan Kumar
 
Solr から使う OpenNLP の日本語固有表現抽出
Solr から使う OpenNLP の日本語固有表現抽出Solr から使う OpenNLP の日本語固有表現抽出
Solr から使う OpenNLP の日本語固有表現抽出Koji Sekiguchi
 
Just-In-Time Compiler in PHP 8
Just-In-Time Compiler in PHP 8Just-In-Time Compiler in PHP 8
Just-In-Time Compiler in PHP 8Nikita Popov
 
Introduction to elasticsearch
Introduction to elasticsearchIntroduction to elasticsearch
Introduction to elasticsearchpmanvi
 
Functional Programming 101 with Scala and ZIO @FunctionalWorld
Functional Programming 101 with Scala and ZIO @FunctionalWorldFunctional Programming 101 with Scala and ZIO @FunctionalWorld
Functional Programming 101 with Scala and ZIO @FunctionalWorldJorge Vásquez
 
Redo log improvements MYSQL 8.0
Redo log improvements MYSQL 8.0Redo log improvements MYSQL 8.0
Redo log improvements MYSQL 8.0Mydbops
 
[AKIBA.AWS] EC2の基礎 - パフォーマンスを100%引き出すオプション設定 -
[AKIBA.AWS] EC2の基礎 - パフォーマンスを100%引き出すオプション設定 -[AKIBA.AWS] EC2の基礎 - パフォーマンスを100%引き出すオプション設定 -
[AKIBA.AWS] EC2の基礎 - パフォーマンスを100%引き出すオプション設定 -Shuji Kikuchi
 
はじめてのSpring Boot
はじめてのSpring BootはじめてのSpring Boot
はじめてのSpring Bootなべ
 
ステップ・バイ・ステップで学ぶラムダ式・Stream api入門 #jjug ccc #ccc h2
ステップ・バイ・ステップで学ぶラムダ式・Stream api入門 #jjug ccc #ccc h2ステップ・バイ・ステップで学ぶラムダ式・Stream api入門 #jjug ccc #ccc h2
ステップ・バイ・ステップで学ぶラムダ式・Stream api入門 #jjug ccc #ccc h2Masatoshi Tada
 
MySQL InnoDB Cluster - New Features in 8.0 Releases - Best Practices
MySQL InnoDB Cluster - New Features in 8.0 Releases - Best PracticesMySQL InnoDB Cluster - New Features in 8.0 Releases - Best Practices
MySQL InnoDB Cluster - New Features in 8.0 Releases - Best PracticesKenny Gryp
 

What's hot (20)

Spring bootでweb 基本編
Spring bootでweb 基本編Spring bootでweb 基本編
Spring bootでweb 基本編
 
Elasticsearch Query DSL - Not just for wizards...
Elasticsearch Query DSL - Not just for wizards...Elasticsearch Query DSL - Not just for wizards...
Elasticsearch Query DSL - Not just for wizards...
 
Reactive design: languages, and paradigms
Reactive design: languages, and paradigmsReactive design: languages, and paradigms
Reactive design: languages, and paradigms
 
Spring Web Service, Spring Integration and Spring Batch
Spring Web Service, Spring Integration and Spring BatchSpring Web Service, Spring Integration and Spring Batch
Spring Web Service, Spring Integration and Spring Batch
 
Capacity Planning For Your Growing MongoDB Cluster
Capacity Planning For Your Growing MongoDB ClusterCapacity Planning For Your Growing MongoDB Cluster
Capacity Planning For Your Growing MongoDB Cluster
 
PostgreSQL and JDBC: striving for high performance
PostgreSQL and JDBC: striving for high performancePostgreSQL and JDBC: striving for high performance
PostgreSQL and JDBC: striving for high performance
 
Fess/Elasticsearchを使った業務で使える?全文検索への道
Fess/Elasticsearchを使った業務で使える?全文検索への道Fess/Elasticsearchを使った業務で使える?全文検索への道
Fess/Elasticsearchを使った業務で使える?全文検索への道
 
Micrometer/Prometheusによる大規模システムモニタリング #jsug #sf_26
Micrometer/Prometheusによる大規模システムモニタリング #jsug #sf_26Micrometer/Prometheusによる大規模システムモニタリング #jsug #sf_26
Micrometer/Prometheusによる大規模システムモニタリング #jsug #sf_26
 
Planet-scale Data Ingestion Pipeline: Bigdam
Planet-scale Data Ingestion Pipeline: BigdamPlanet-scale Data Ingestion Pipeline: Bigdam
Planet-scale Data Ingestion Pipeline: Bigdam
 
Developing RESTful Web APIs with Python, Flask and MongoDB
Developing RESTful Web APIs with Python, Flask and MongoDBDeveloping RESTful Web APIs with Python, Flask and MongoDB
Developing RESTful Web APIs with Python, Flask and MongoDB
 
My sql 5.6 master slave and master-master replication.step by step configurat...
My sql 5.6 master slave and master-master replication.step by step configurat...My sql 5.6 master slave and master-master replication.step by step configurat...
My sql 5.6 master slave and master-master replication.step by step configurat...
 
Solr から使う OpenNLP の日本語固有表現抽出
Solr から使う OpenNLP の日本語固有表現抽出Solr から使う OpenNLP の日本語固有表現抽出
Solr から使う OpenNLP の日本語固有表現抽出
 
Just-In-Time Compiler in PHP 8
Just-In-Time Compiler in PHP 8Just-In-Time Compiler in PHP 8
Just-In-Time Compiler in PHP 8
 
Introduction to elasticsearch
Introduction to elasticsearchIntroduction to elasticsearch
Introduction to elasticsearch
 
Functional Programming 101 with Scala and ZIO @FunctionalWorld
Functional Programming 101 with Scala and ZIO @FunctionalWorldFunctional Programming 101 with Scala and ZIO @FunctionalWorld
Functional Programming 101 with Scala and ZIO @FunctionalWorld
 
Redo log improvements MYSQL 8.0
Redo log improvements MYSQL 8.0Redo log improvements MYSQL 8.0
Redo log improvements MYSQL 8.0
 
[AKIBA.AWS] EC2の基礎 - パフォーマンスを100%引き出すオプション設定 -
[AKIBA.AWS] EC2の基礎 - パフォーマンスを100%引き出すオプション設定 -[AKIBA.AWS] EC2の基礎 - パフォーマンスを100%引き出すオプション設定 -
[AKIBA.AWS] EC2の基礎 - パフォーマンスを100%引き出すオプション設定 -
 
はじめてのSpring Boot
はじめてのSpring BootはじめてのSpring Boot
はじめてのSpring Boot
 
ステップ・バイ・ステップで学ぶラムダ式・Stream api入門 #jjug ccc #ccc h2
ステップ・バイ・ステップで学ぶラムダ式・Stream api入門 #jjug ccc #ccc h2ステップ・バイ・ステップで学ぶラムダ式・Stream api入門 #jjug ccc #ccc h2
ステップ・バイ・ステップで学ぶラムダ式・Stream api入門 #jjug ccc #ccc h2
 
MySQL InnoDB Cluster - New Features in 8.0 Releases - Best Practices
MySQL InnoDB Cluster - New Features in 8.0 Releases - Best PracticesMySQL InnoDB Cluster - New Features in 8.0 Releases - Best Practices
MySQL InnoDB Cluster - New Features in 8.0 Releases - Best Practices
 

Similar to Rest API using Flask & SqlAlchemy

RESTful API 제대로 만들기
RESTful API 제대로 만들기RESTful API 제대로 만들기
RESTful API 제대로 만들기Juwon Kim
 
PHP and Rich Internet Applications
PHP and Rich Internet ApplicationsPHP and Rich Internet Applications
PHP and Rich Internet Applicationselliando dias
 
Cakefest 2010: API Development
Cakefest 2010: API DevelopmentCakefest 2010: API Development
Cakefest 2010: API DevelopmentAndrew Curioso
 
[PL] Jak nie zostać "programistą" PHP?
[PL] Jak nie zostać "programistą" PHP?[PL] Jak nie zostać "programistą" PHP?
[PL] Jak nie zostać "programistą" PHP?Radek Benkel
 
Positive Hack Days. Goltsev. Web Vulnerabilities: Difficult Cases
Positive Hack Days. Goltsev. Web Vulnerabilities: Difficult CasesPositive Hack Days. Goltsev. Web Vulnerabilities: Difficult Cases
Positive Hack Days. Goltsev. Web Vulnerabilities: Difficult CasesPositive Hack Days
 
20th.陈晓鸣 百度海量日志分析架构及处理经验分享
20th.陈晓鸣 百度海量日志分析架构及处理经验分享20th.陈晓鸣 百度海量日志分析架构及处理经验分享
20th.陈晓鸣 百度海量日志分析架构及处理经验分享elevenma
 
Web注入+http漏洞等描述
Web注入+http漏洞等描述Web注入+http漏洞等描述
Web注入+http漏洞等描述fangjiafu
 
PHP and Rich Internet Applications
PHP and Rich Internet ApplicationsPHP and Rich Internet Applications
PHP and Rich Internet Applicationselliando dias
 
Ruby on Rails: Tasty Burgers
Ruby on Rails: Tasty BurgersRuby on Rails: Tasty Burgers
Ruby on Rails: Tasty BurgersAaron Patterson
 
How to build a High Performance PSGI/Plack Server
How to build a High Performance PSGI/Plack Server How to build a High Performance PSGI/Plack Server
How to build a High Performance PSGI/Plack Server Masahiro Nagano
 
Bootstrat REST APIs with Laravel 5
Bootstrat REST APIs with Laravel 5Bootstrat REST APIs with Laravel 5
Bootstrat REST APIs with Laravel 5Elena Kolevska
 
REST with Eve and Python
REST with Eve and PythonREST with Eve and Python
REST with Eve and PythonPiXeL16
 
Anatomy of a PHP Request ( UTOSC 2010 )
Anatomy of a PHP Request ( UTOSC 2010 )Anatomy of a PHP Request ( UTOSC 2010 )
Anatomy of a PHP Request ( UTOSC 2010 )Joseph Scott
 
Make WordPress realtime.
Make WordPress realtime.Make WordPress realtime.
Make WordPress realtime.Josh Hillier
 
Debugging: Rules And Tools - PHPTek 11 Version
Debugging: Rules And Tools - PHPTek 11 VersionDebugging: Rules And Tools - PHPTek 11 Version
Debugging: Rules And Tools - PHPTek 11 VersionIan Barber
 
Roll Your Own API Management Platform with nginx and Lua
Roll Your Own API Management Platform with nginx and LuaRoll Your Own API Management Platform with nginx and Lua
Roll Your Own API Management Platform with nginx and LuaJon Moore
 
Analyzing Log Data With Apache Spark
Analyzing Log Data With Apache SparkAnalyzing Log Data With Apache Spark
Analyzing Log Data With Apache SparkSpark Summit
 
The Django Web Application Framework
The Django Web Application FrameworkThe Django Web Application Framework
The Django Web Application FrameworkSimon Willison
 
Automated infrastructure is on the menu
Automated infrastructure is on the menuAutomated infrastructure is on the menu
Automated infrastructure is on the menujtimberman
 

Similar to Rest API using Flask & SqlAlchemy (20)

RESTful API 제대로 만들기
RESTful API 제대로 만들기RESTful API 제대로 만들기
RESTful API 제대로 만들기
 
PHP and Rich Internet Applications
PHP and Rich Internet ApplicationsPHP and Rich Internet Applications
PHP and Rich Internet Applications
 
Cakefest 2010: API Development
Cakefest 2010: API DevelopmentCakefest 2010: API Development
Cakefest 2010: API Development
 
[PL] Jak nie zostać "programistą" PHP?
[PL] Jak nie zostać "programistą" PHP?[PL] Jak nie zostać "programistą" PHP?
[PL] Jak nie zostać "programistą" PHP?
 
Positive Hack Days. Goltsev. Web Vulnerabilities: Difficult Cases
Positive Hack Days. Goltsev. Web Vulnerabilities: Difficult CasesPositive Hack Days. Goltsev. Web Vulnerabilities: Difficult Cases
Positive Hack Days. Goltsev. Web Vulnerabilities: Difficult Cases
 
20th.陈晓鸣 百度海量日志分析架构及处理经验分享
20th.陈晓鸣 百度海量日志分析架构及处理经验分享20th.陈晓鸣 百度海量日志分析架构及处理经验分享
20th.陈晓鸣 百度海量日志分析架构及处理经验分享
 
Web注入+http漏洞等描述
Web注入+http漏洞等描述Web注入+http漏洞等描述
Web注入+http漏洞等描述
 
PHP and Rich Internet Applications
PHP and Rich Internet ApplicationsPHP and Rich Internet Applications
PHP and Rich Internet Applications
 
Ruby on Rails: Tasty Burgers
Ruby on Rails: Tasty BurgersRuby on Rails: Tasty Burgers
Ruby on Rails: Tasty Burgers
 
Os Pruett
Os PruettOs Pruett
Os Pruett
 
How to build a High Performance PSGI/Plack Server
How to build a High Performance PSGI/Plack Server How to build a High Performance PSGI/Plack Server
How to build a High Performance PSGI/Plack Server
 
Bootstrat REST APIs with Laravel 5
Bootstrat REST APIs with Laravel 5Bootstrat REST APIs with Laravel 5
Bootstrat REST APIs with Laravel 5
 
REST with Eve and Python
REST with Eve and PythonREST with Eve and Python
REST with Eve and Python
 
Anatomy of a PHP Request ( UTOSC 2010 )
Anatomy of a PHP Request ( UTOSC 2010 )Anatomy of a PHP Request ( UTOSC 2010 )
Anatomy of a PHP Request ( UTOSC 2010 )
 
Make WordPress realtime.
Make WordPress realtime.Make WordPress realtime.
Make WordPress realtime.
 
Debugging: Rules And Tools - PHPTek 11 Version
Debugging: Rules And Tools - PHPTek 11 VersionDebugging: Rules And Tools - PHPTek 11 Version
Debugging: Rules And Tools - PHPTek 11 Version
 
Roll Your Own API Management Platform with nginx and Lua
Roll Your Own API Management Platform with nginx and LuaRoll Your Own API Management Platform with nginx and Lua
Roll Your Own API Management Platform with nginx and Lua
 
Analyzing Log Data With Apache Spark
Analyzing Log Data With Apache SparkAnalyzing Log Data With Apache Spark
Analyzing Log Data With Apache Spark
 
The Django Web Application Framework
The Django Web Application FrameworkThe Django Web Application Framework
The Django Web Application Framework
 
Automated infrastructure is on the menu
Automated infrastructure is on the menuAutomated infrastructure is on the menu
Automated infrastructure is on the menu
 

Recently uploaded

%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...masabamasaba
 
Artyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptxArtyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptxAnnaArtyushina1
 
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital TransformationWSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital TransformationWSO2
 
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfonteinmasabamasaba
 
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...Bert Jan Schrijver
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...masabamasaba
 
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...masabamasaba
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...masabamasaba
 
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...masabamasaba
 
Architecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the pastArchitecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the pastPapp Krisztián
 
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...WSO2
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park masabamasaba
 
WSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go PlatformlessWSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go PlatformlessWSO2
 
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburgmasabamasaba
 
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdf
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdfPayment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdf
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdfkalichargn70th171
 
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...masabamasaba
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension AidPhilip Schwarz
 
AI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplateAI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplatePresentation.STUDIO
 

Recently uploaded (20)

%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
 
Artyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptxArtyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptx
 
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital TransformationWSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
 
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
 
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
 
Microsoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdfMicrosoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdf
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
 
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
 
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
 
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
 
Architecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the pastArchitecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the past
 
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
 
WSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go PlatformlessWSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go Platformless
 
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
 
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdf
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdfPayment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdf
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdf
 
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
 
AI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplateAI & Machine Learning Presentation Template
AI & Machine Learning Presentation Template
 

Rest API using Flask & SqlAlchemy

  • 1. REST APIREST API USING FLASK & SQLALCHEMYUSING FLASK & SQLALCHEMY Alessandro Cucci Python Developer, Energee3
  • 2. REPRESENTATIONAL STATE TRANSFERREPRESENTATIONAL STATE TRANSFER ROY THOMAS FIELDING - 2010ROY THOMAS FIELDING - 2010 HTTP://WWW.ICS.UCI.EDU/~FIELDING/PUBS/DISSERTATION/REST_ARCH_STYLE.HTMHTTP://WWW.ICS.UCI.EDU/~FIELDING/PUBS/DISSERTATION/REST_ARCH_STYLE.HTM
  • 4. RESTREST GUIDING CONSTRAINTSGUIDING CONSTRAINTS UNIFORM INTERFACEUNIFORM INTERFACE IDENTIFICATION OF RESOURCESIDENTIFICATION OF RESOURCES MANIPULATION OF RESOURCES THROUGH REPRESENTATIONSMANIPULATION OF RESOURCES THROUGH REPRESENTATIONS SELF-DESCRIPTIVE MESSAGESSELF-DESCRIPTIVE MESSAGES HYPERMEDIA AS THE ENGINE OF APPLICATION STATEHYPERMEDIA AS THE ENGINE OF APPLICATION STATE
  • 5. REST URI EXAMPLESREST URI EXAMPLES h�p://myapi.com/customers h�p://myapi.com/customers/33245 REST ANTI-PATTERNSREST ANTI-PATTERNS h�p://myapi.com/update_customer&id=12345& format=json h�p://myapi.com/customers/12345/update
  • 6. RELATIONSHIP BETWEEN URL AND HTTPRELATIONSHIP BETWEEN URL AND HTTP METHODSMETHODS URL GET PUT POST DELETE h�p://api.myvinylcollec�on.com /records/ LIST of records in collec�on Method not allowed. CREATE a new entry in the collec�on. DELETE the en�re collec�on. h�p://api.myvinylcollec�on.com /records/1 RETRIEVE a representa�on of the addressed member of the collec�on REPLACE the addressed member of the collec�on. Method not allowed. DELETE the addressed member of the collec�on.
  • 7. WHAT DO WE NOT CARE FOR THIS EVENINGWHAT DO WE NOT CARE FOR THIS EVENING Stability & Tes�ng Long-Term maintainability Edge Cases Opera�ons, Caching & Deployment
  • 8. MY VINYL COLLECTION APIMY VINYL COLLECTION API
  • 10. HELLO API!HELLO API! from flask import Flask app = Flask(__name__) if __name__ == '__main__': app.run() $ python myvinylcollectionapi.py * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
  • 11.
  • 12. HELLO API!HELLO API! from flask import Flask app = Flask(__name__) @app.route("/") def hello(): return "Hello PyRE!" if __name__ == '__main__': app.run()
  • 13. HELLO API!HELLO API! from flask import Flask, jsonify app = Flask(__name__) @app.route("/") def hello(): return jsonify(data="Hello PyRE!") if __name__ == '__main__': app.run()
  • 14. HTTP GET METHODSHTTP GET METHODS URL GET h�p://api.myvinylcollec�on.com /records LIST of records in collec�on h�p://api.myvinylcollec�on.com /records/1 RETRIEVE a representa�on of the addressed member of the collec�on
  • 15. from flask import Flask, jsonify, abort app = Flask(__name__) RECORDS = [ { 'id': 0, 'artist': "Queen", 'title': "A Night At The Opera", 'year': "1975", 'label': "EMI" }, { 'id': 1, 'artist': "Pink Floyd", 'title': "The Dark Side Of The Moon", 'year': "1989", 'label': "EMI" }, ... ]
  • 16. @app.route("/records") def get_records(): return jsonify(RECORDS) @app.route("/records/<int:index>") def get_record(index): try: record = RECORDS[index] except IndexError: abort(404) return jsonify(record) if __name__ == '__main__': app.run()
  • 17. HTTP GET METHODHTTP GET METHOD $ curl -X GET localhost:5000/records [ { "artist": "Queen", "id": 0, "label": "EMI", "title": "A Night At The Opera", "year": "1975" }, { "artist": "Pink Floyd", "id": 1, "label": "EMI", "title": "The Dark Side Of The Moon", "year": "1989" } ]
  • 18. HTTP GET Method $ curl -X GET localhost:5000/records/1 { "artist": "Pink Floyd", "id": 1, "label": "EMI", "title": "The Dark Side Of The Moon", "year": "1989" } $ curl -X GET localhost:5000/records/5 <title>404 Not Found</title> <h1>Not Found</h1> <p>The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.</p>
  • 19. JSONIFY THAT ERROR!JSONIFY THAT ERROR! @app.errorhandler(404) def page_not_found(error): return jsonify( error="Not Found", status_code=404 ), 404 $ curl -X GET localhost:5000/records/5 { "error": "Not Found", "status_code": 404 }
  • 20. GOOD NEWS:GOOD NEWS: IT WORKS!IT WORKS! BAD NEWS:BAD NEWS: STATIC DATASTATIC DATA
  • 22. SQLITESQLITE + DISCOGS.COM+ DISCOGS.COM + PANDAS+ PANDAS
  • 23. CREATE TABLE "collection" ( `index` INTEGER PRIMARY KEY AUTOINCREMENT, `Catalog#` TEXT, `Artist` TEXT, `Title` TEXT, `Label` TEXT, `Format` TEXT, `Rating` REAL, `Released` INTEGER, `release_id` INTEGER, `CollectionFolder` TEXT, `Date Added` TEXT, `Collection Media Condition` TEXT, `Collection Sleeve Condition` TEXT, `Collection Notes` REAL )
  • 24. CSV COLLECTION EXPORTCSV COLLECTION EXPORT
  • 25. import pandas import sqlite3 conn = sqlite3.connect('record_collection.db') conn.text_factory = sqlite3.Binary df = pandas.read_csv('collection.csv') df.to_sql('collection', conn)
  • 26.
  • 27. OBJECT RELATIONAL MAPPER (ORM)OBJECT RELATIONAL MAPPER (ORM) HTTP://DOCS.SQLALCHEMY.ORGHTTP://DOCS.SQLALCHEMY.ORG
  • 28. MODEL.PYMODEL.PY from flask_sqlalchemy import SQLAlchemy from flask_sqlalchemy import orm db = SQLAlchemy() class Record(db.Model): __tablename__ = "collection" index = db.Column(db.Integer, primary_key=True) Artist = db.Column(db.Text, nullable=False) Title = db.Column(db.Text, nullable=False) Label = db.Column(db.Text) Released = db.Column(db.Text) def as_dict(self): columns = orm.class_mapper(self.__class__).mapped_table.c return { col.name: getattr(self, col.name) for col in columns }
  • 29. QUERYQUERY >>> # .all() return a list >>> all_records = Record.query.all() >>> len(all_records) 80 >>> # .first() return the first item that matches >>> record = Record.query.filter(Record.index == 9).first() >>> record.Title "Back In Black" >>> record.Artist "AC/DC" >>> record.Released "1980" >>> # .filter_by() is a shortcut >>> record = Record.query.filter_by(index == 6).first() >>> record.Title "Hotel California"
  • 30. from flask import Flask, jsonify from model import db, Record app = Flask(__name__) app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///record_collection.db" db.init_app(app) @app.route("/records") def get_records(): records = [r.as_dict() for r in Record.query.all()] return jsonify(records) @app.route("/records/<int:index>") def get_record(index): record = Record.query.filter(Record.index == index).first_or_404() return jsonify(record.as_dict())
  • 31. $ curl -X GET localhost:5000/records [ { "Artist": "The Police", "index": 0, "Title": "Reggatta De Blanc" }, { "Artist": "The Beatles", "index": 1, "Title": "Abbey Road" }, ... ] $ curl -X GET localhost:5000/records/1 { "Artist": "The Beatles", "index": 1, "Title": "Abbey Road" }
  • 32. HTTP POST METHODSHTTP POST METHODS URL POST h�p://api.myvinylcollec�on.com /records/ CREATE a new entry in the collec�on. h�p://api.myvinylcollec�on.com /records/1 Method not allowed.
  • 33. POST ON LOCALHOST:5000/RECORDS/IDPOST ON LOCALHOST:5000/RECORDS/ID @app.errorhandler(405) def method_not_allowed(error): return jsonify( error="Method Not Allowed", status_code=405 ), 405 from flask import Flask, jsonify, abort, request ... @app.route("/records/<int:index>", methods=['GET', 'POST']) def get_record(index): if request.method == 'POST': abort(405) record = Record.query.filter(Record.index == index).first_or_404() return jsonify(record.as_dict())
  • 34. $ curl -X POST localhost:5000/records/1 { "error": "Method Not Allowed", "status_code": 405 }
  • 35. INSERT INTO DATABASEINSERT INTO DATABASE >>> # .add() insert a record >>> db.session.add(record) >>> # changes won't be saved until committed! >>> db.session.commit()
  • 36. ADDING A RECORD TO MY COLLECTIONADDING A RECORD TO MY COLLECTION @app.route("/records", methods=['GET', 'POST']) def get_records(): if request.method == 'POST': record = Record(**json.loads(request.data)) db.session.add(record) db.session.commit() return jsonify(record.as_dict()), 201 records = [r.as_dict() for r in Record.query.all()] return jsonify(records)
  • 37. ADDING A RECORD TO MY COLLECTIONADDING A RECORD TO MY COLLECTION $ curl -i -H "Content-Type: application/json" -X POST localhost:5000/records > -d '{"Artist":"Neil Joung", "Title":"Harvest", > "Label":"Reprise Records", "Released":"1977"}' HTTP/1.0 201 CREATED Content-Type: application/json Content-Length: 104 Server: Werkzeug/0.11.11 Python/2.7.12+ Date: Sat, 03 Dec 2016 11:03:10 GMT { "Artist": "Neil Young", "Label": "Reprise Records", "Released": "1977", "Title": "American Stars 'N Bars", "index": 91 }
  • 38. HTTP PUT METHODSHTTP PUT METHODS URL PUT h�p://api.myvinylcollec�on.com /records/ Method not allowed. h�p://api.myvinylcollec�on.com /records/1 REPLACE the addressed member of the collec�on.
  • 39. @app.route("/records", methods=['GET', 'POST', 'PUT']) def get_records(): if request.method == 'POST': record = Record(**json.loads(request.data)) db.session.add(record) db.session.commit() return jsonify(record.as_dict()), 201 elif request.method == 'PUT': abort(405) records = [r.as_dict() for r in Record.query.all()] return jsonify(records), 200 @app.route("/records/<int:index>", methods=['GET', 'POST', 'PUT']) def get_record(index): if request.method == 'POST': abort(405) else: record = Record.query.filter(Record.index == index).first_or_404() if request.method == 'PUT': for k, v in json.loads(request.data).iteritems(): setattr(record, k, v) db.session.add(record) db.session.commit() return jsonify(record.as_dict()), 200
  • 40. PUT ON COLLECTIONPUT ON COLLECTION $ curl -i -H "Content-Type: application/json" > -X POST localhost:5000/records > -d '{"Artist":"Neil Joung", "Title":"Harvest", > "Label":"Reprise Records", "Released":"1977"}' HTTP/1.0 405 METHOD NOT ALLOWED Content-Type: application/json Content-Length: 59 Server: Werkzeug/0.11.11 Python/2.7.12+ Date: Sat, 03 Dec 2016 10:20:06 GMT { "error": "Method Not Allowed", "status_code": 405 }
  • 41. PUT ON RESOURCEPUT ON RESOURCE $ curl -i -H "Content-Type: application/json" > -X PUT localhost:5000/records/91 > -d '{"Artist":"Neil Joung", "Title":"Harvest", > "Label":"Reprise Records", "Released":"1977"}' HTTP/1.0 200 OK Content-Type: application/json Content-Length: 104 Server: Werkzeug/0.11.11 Python/2.7.12+ Date: Sat, 03 Dec 2016 11:07:22 GMT { "Artist": "Neil Young", "Label": "Reprise Records", "Released": "1977", "Title": "American Stars 'N Bars", "index": 91 }
  • 42. HTTP DELETE METHODSHTTP DELETE METHODS URL DELETE h�p://api.myvinylcollec�on.com /records/ DELETE the en�re collec�on. h�p://api.myvinylcollec�on.com /records/1 DELETE the addressed member of the collec�on.
  • 43. DELETE ON COLLECTIONDELETE ON COLLECTION @app.route("/records", methods=['GET', 'POST', 'PUT', 'DELETE']) def get_records(): if request.method == 'POST': record = Record(**json.loads(request.data)) db.session.add(record) db.session.commit() return jsonify(record.as_dict()), 201 elif request.method == 'PUT': abort(405) records = [r.as_dict() for r in Record.query.all()] if request.method == 'DELETE': for r in records: db.session.delete(r) db.session.commit() records = [r.as_dict() for r in Record.query.all()] return jsonify(records), 200
  • 44. DELETE ON RESOURCEDELETE ON RESOURCE @app.route("/records/<int:index>", methods=['GET', 'POST', 'PUT', 'DELETE']) def get_record(index): if request.method == 'POST': abort(405) else: record = Record.query.filter(Record.index == index).first_or_404() if request.method == 'PUT': for k, v in json.loads(request.data).iteritems(): setattr(record, k, v) db.session.add(record) db.session.commit() elif request.method == 'DELETE': db.session.delete(record) db.session.commit() return jsonify(record.as_dict()), 200
  • 45. DELETE ON RESOURCEDELETE ON RESOURCE $ curl -i -X DELETE localhost:5000/records/91 HTTP/1.0 200 OK Content-Type: application/json Content-Length: 104 Server: Werkzeug/0.11.11 Python/2.7.12+ Date: Sat, 03 Dec 2016 10:40:00 GMT { "Artist": "Neil Young", "Label": "Reprise Records", "Released": "1977", "Title": "American Stars 'N Bars", "index": 91 }
  • 46. DELETE ON RESOURCEDELETE ON RESOURCE $ curl -i -X DELETE localhost:5000/records/91 HTTP/1.0 HTTP/1.0 404 NOT FOUND Content-Type: application/json Content-Length: 50 Server: Werkzeug/0.11.11 Python/2.7.12+ Date: Sat, 03 Dec 2016 10:40:09 GMT { "error": "Not Found", "status_code": 404 }
  • 47. DELETE ON COLLECTIONDELETE ON COLLECTION $ curl -i -X DELETE localhost:5000/records
  • 48.
  • 50. PWD AUTHENTICATIONPWD AUTHENTICATION from flask import Flask, jsonify, abort from flask_login import LoginManager, current_user app = Flask(__name__) login_manager = LoginManager(app) @login_manager.request_loader def check_token(request): token = request.headers.get('Authorization') if token == 'L3T_M3_PA55!': return "You_can_pass" # DON'T TRY THIS AT HOME! return None @app.route("/") def get_main_root(): if current_user: return jsonify(data='Hello Login'), 200 else: abort(401)
  • 51. HOW IT WORKSHOW IT WORKS $ curl -i localhost:5000 HTTP/1.0 401 UNAUTHORIZED Content-Type: application/json WWW-Authenticate: Basic realm="Authentication Required" Content-Length: 37 Server: Werkzeug/0.11.11 Python/2.7.12+ Date: Sat, 03 Dec 2016 14:46:55 GMT { "error": "Unauthorized access" } $ curl -i -H "Authorization: L3T_M3_PA55!" localhost:5000 HTTP/1.0 200 OK Content-Type: application/json Content-Length: 28 Server: Werkzeug/0.11.11 Python/2.7.12+ Date: Sat, 03 Dec 2016 14:42:00 GMT { "data": "Hello Login" }
  • 52. SECURING OUR API - RESOURCESECURING OUR API - RESOURCE @app.route("/records/<int:index>", methods=['GET', 'POST', 'PUT', 'DELETE']) def get_record(index): if request.method == 'POST': abort(405) else: record = Record.query.filter(Record.index == index).first_or_404() if request.method == 'PUT': if current_user: for k, v in json.loads(request.data).iteritems(): setattr(record, k, v) db.session.add(record) db.session.commit() else: abort(401) elif request.method == 'DELETE': if current_user: db.session.delete(record) db.session.commit() else: abort(401) return jsonify(record.as_dict()), 200
  • 53. SECURING OUR API - COLLECTIONSECURING OUR API - COLLECTION @app.route("/records", methods=['GET', 'POST', 'PUT', 'DELETE']) def get_records(): if request.method == 'POST': record = Record(**json.loads(request.data)) db.session.add(record) db.session.commit() return jsonify(record.as_dict()), 201 elif request.method == 'PUT': abort(405) records = [r.as_dict() for r in Record.query.all()] if request.method == 'DELETE': if current_user: for r in records: db.session.delete(r) db.session.commit() records = [r.as_dict() for r in Record.query.all()] return jsonify(records), 200 else: abort(401) return jsonify(records), 200
  • 54. HOMEWORKSHOMEWORKS Pagina�on with Flask-SqlAlchemy Rate Limi�ng with Flask-Limiter Cache with Flask-Cache
  • 55. THANK YOU!THANK YOU! { 'slides': 'www.alessandrocucci.it/pyre/restapi', 'code': 'https://goo.gl/4UOqEr' }