0% found this document useful (0 votes)
125 views

Building Beautiful Restful Apis Using Flask 1

The document describes how to build beautiful REST APIs with Flask, Flask-RESTPlus and OpenAPI. It covers topics like REST conventions, defining API routes and models, request and response handling including parsing input, validation, formatting output and handling exceptions.

Uploaded by

Ezhil
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
125 views

Building Beautiful Restful Apis Using Flask 1

The document describes how to build beautiful REST APIs with Flask, Flask-RESTPlus and OpenAPI. It covers topics like REST conventions, defining API routes and models, request and response handling including parsing input, validation, formatting output and handling exceptions.

Uploaded by

Ezhil
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 34

BUILDING BEAUTIFUL REST APIs

with Flask, Swagger UI and Flask-RESTPlus

Michał Karzyński • EuroPython 2016


ABOUT ME

• My name is Michał Karzyński (that’s Polish for Mike)

• I blog at http://michal.karzynski.pl

Short URL: karzyn.com

• I wrote a book for Linux admins, I write code in Python and JavaScript

• I’m the tech lead of a Web UI team at


WHAT IS A WEB API?

Web (JavaScript)
API
(JSON)

Server

(Python)
Phone (Swift, Java)
WHAT IS A REST API?
REPRESENTATIONAL STATE TRANSFER

A clever way to use HTTP to build APIs.


ANATOMY OF HTTP

Method Path Query Status Code

Headers Headers

Body Body

Request Response
GET ?search=Moby Dick
POST
PUT /api/books Cookies… 200 OK
DELETE
404 Not Found

Method Path Query Status Code

Headers Headers

Body Body

Request JSON Response


Method Path Query
Headers

REST CONVENTIONS Body

GET PUT POST DELETE

Collection
 List books New book


/books

Item
 Display book Update book Delete book


/books/123

Controller Borrow book


/books/123/borrow
FLASK

flask.pocoo.org
FLASK-RESTPlus
• define and document endpoints

• validate input

• format output (as JSON)

• turn Python exceptions into HTTP responses

• minimise boilerplate code


flask-restplus.rtfd.io
• generate interactive documentation (Swagger UI)
Demo
OPEN API FORMAT
OPEN API SPECIFICATION
• Standard language to describe REST APIs

• Open source (Linux Foundation)

• Tools:

• Swagger UI

• Swagger Editor

• Code generators

• Initiative with many powerful members

openapis.org swagger.io
OPEN API SPECIFICATION
• Standard language to describe REST APIs

• Open source (Linux Foundation)

• Tools:

• Swagger UI

• Swagger Editor

• Code generators

• Initiative with many powerful members

openapis.org swagger.io
Method Path Query

Headers

Body

Request
Method Path Query

REQUEST METHOD Headers

POST /api/books/123/borrow?when=today Body

from flask_restplus import Resource

@api.route('/<int:id>/borrow')
class BorrowBookController(Resource):


def post(self, id):

""" Borrow book from library.


Allows the current user to borrow
the book out of the library.

"""

...
return {'message': 'OK'}
Method Path Query

REQUEST METHOD Headers

POST /api/books/123/borrow?when=today Body

from flask_restplus import Resource

@api.route('/<int:id>/borrow')
class BorrowBookController(Resource):


 class Resource:

def post(self, id):
 

""" Borrow book from library.
 def get(self)...


 def post(self)...

Allows the current user to borrow def put(self)...

the book out of the library.
 def delete(self)...

"""
 def patch(self)...

... def options(self)...
return {'message': 'OK'} def head(self)...

Method Path Query

REQUEST METHOD Headers

POST /api/books/123/borrow?when=today Body


Method Path Query

REQUEST METHOD Headers

POST /api/books/123/borrow?when=today Body


Method Path Query

REQUEST PATH Headers

POST /api/books/123/borrow?when=today Body

@api.route(‘/books/<int:id>/borrow’)

@api.route('/articles/<title>')

@api.route('/wiki/<path:wikipage>')

@api.route('/values/<float:value>')

@api.route('/object/<uuid:identifier>')
Method Path Query

REQUEST PATH Headers

GET /api/book/123/borrow?when=today Body


Method Path Query

QUERY ARGUMENTS Headers

GET /api/books?page=1&per_page=10 Body

from flask_restplus import reqparse




pagination = reqparse.RequestParser()

pagination.add_argument('page', type=int, required=False,
default=1, help='Page number')

pagination.add_argument('per_page', type=int, required=False,
choices=[10, 20, 30, 40, 50])

Method Path Query

QUERY ARGUMENTS Headers

GET /api/books?page=1&per_page=10 Body

from flask import request



from flask_restplus import Resource


@api.route('/')

class PostsCollection(Resource):


@api.expect(parsers.pagination)

def get(self):

args = pagination_arguments.parse_args(request)

page = args.get('page', 1)

per_page = args.get('per_page', 10)

...
Method Path Query

QUERY ARGUMENTS Headers

GET /api/books?page=1&per_page=10 Body


Method Path Query

REQUEST BODY (JSON)
 Headers

API MODELS Body

blog_post = api.model('Blog post', {



'title': fields.String(description='Article title'),

'body': fields.String(description='Article content'),

'pub_date': fields.DateTime,

'category_id': fields.Integer(min=1),

})

@api.expect(blog_post)

def post(self):

...
Method Path Query

REQUEST BODY (JSON)
 Headers

API MODELS Body


Method Path Query

API MODELS
 Headers

INHERITANCE AND NESTING Body

category = api.model('Blog category', {



'id': fields.Integer(description='The unique id of category'),

'name': fields.String(description='Category name'),

})


category_with_posts = api.inherit('Blog category with posts', category,
{

'posts': fields.List(fields.Nested(blog_post))

})
Status Code

Headers

Body

Response
Status Code
Headers

RESPONSE STATUS CODE Body

@api.route('/<int:id>')

@api.response(404, 'Post not found.')

class PostItem(Resource):


@api.response(204, 'Post successfully deleted.')

def delete(self, id):

"""

Deletes blog post.

"""

delete_post(id)

return None, 204
Status Code
Headers

RESPONSE STATUS CODE Body


Status Code
Headers

RESPONSE BODY (JSON) Body

blog_post = api.model('Blog post', {


...

'category_id': fields.Integer(attribute='category.id'),

'name': fields.String(attribute=lambda x: x._private_name),



})

@api.marshal_with(blog_post)
 @api.marshal_list_with(blog_post)

def get(self):
 def get(self):

... ...
EXCEPTION HANDLING

from sqlalchemy.orm.exc import NoResultFound




@api.errorhandler(NoResultFound)

def database_not_found_error_handler(e):

log.warning(traceback.format_exc())

return {'message': 'A database result was not found.'}, 404
INERACTIVE
DEBUGGER
from flask import Flask

from flask_restplus import Resource, Api


app = Flask(__name__)

api = Api(app)


@api.route('/hello')

class HelloWorld(Resource):

def get(self):

return {'hello': 'world'}


if __name__ == '__main__':

app.run(debug=True)
Demo code and article available on my blog:

karzyn.com

THANK YOU

You might also like