I have been writing API's with flask for some time now and didn't realize what method I was using and if it was the best for me. Because of this I wasn't able to fully understand and grow exponentially as a backend engineer. After some research, I found out I was writing these API's the flask "RESTless" way.
Oh, I am a bit confused what are flask-RESTless and RESTful ?
Flask-Restless is a method of writing API's that enables simple generation of RESTful API's for database models that are using SqlAlchemy, Flask-SqlAlchemy or raw Sql data. The endpoints usually send and receive data in JSON format.
Flask-RESTful simply put is an extension that flask provides for quickly building REST API'S that work with your already existing ORM'S or libraries. The advantage of using this method is that it's quite easy to use, light weight and encourages best practices with minimal setup.
These are both great ways to go , but I would personally recommend flask RESTful simply because it's the more mature and flexible option of the two. It also allows easy integration of other request methods under one endpoint or resource.
Exploring RESTful vs RESTless
We will be writing a simple todo API with flask . We will write this program the flask RESTless way, then proceed to learning how to write it the flask RESTful way.
Prerequisites :
FLask, Flask RESTful
Installing flask and flask-restful
pip install flask
pip install flask-restful
The flask RESTless way
Writing this program the flask RESTless way you would define the route(endpoint) with the request type in it i.e POST ,GET , PUT ,DELETE etc. The function the route would use comes after , in which we return the JSON data.
# Using flask to make an api
# import necessary libraries and functions
from flask import Flask, jsonify, request
# creating a Flask app
app = Flask(__name__)
TODOS = {
'todo1': {'task': 'sleep'},
'todo2': {'task': 'eat'},
'todo3': {'task': 'code!'},
'todo4': {'task': 'repeat'},
}
def abort_if_todo_doesnt_exist(todo_id):
if todo_id not in TODOS:
message="Todo {} doesn't exist".format(todo_id)
return jsonify({'message':message})
@app.route('/todos/<todo_id>', methods = ['GET','DELETE','PUT' ])
def todo(todo_id):
if (request.method == 'GET'):
abort_if_todo_doesnt_exist(todo_id)
return jsonify({'data': TODOS[todo_id]})
elif (request.method == 'DELETE'):
abort_if_todo_doesnt_exist(todo_id)
del TODOS[todo_id]
return jsonify({'message':'You have deleted the todo item'}), 204
elif (request.methof == 'PUT'):
abort_if_todo_doesnt_exist(todo_id)
args = parser.parse_args()
task = {'task': args['task']}
TODOS[todo_id] = task
return jsonify({'task':task}), 201
@app.route('/todos/', methods = ['GET','POST'])
def todo_list():
if (request.method == 'GET'):
return jsonify({'data': TODOS})
elif (request.method == 'POST'):
args = parser.parse_args()
todo_id = int(max(TODOS.keys()).lstrip('todo')) + 1
todo_id = 'todo%i' % todo_id
TODOS[todo_id] = {'task': args['task']}
return jsonify({'data':TODOS[todo_id]}), 201
# driver function
if __name__ == '__main__':
app.run(debug = True)
Above we wrote a simple todo API with endpoints to GET, DELETE and PUT an item in the TODOS dictionary and get a list of all the items in the TODO dictionary. We started by defining a dictionary where all our todo items are stored. We then defined a function "abort_if_todo_doesnt_exist" which collects a parameter "todo_id" to check if a todo item exists in the dictionary. After this, we created a route for our new function todo, which accepts a GET request to find an item in the dictionary, a DELETE request to delete an item from the dictionary (while checking if the item already exists before performing these actions) and a PUT request to add a task to an item to the dictionary . We then defined our second route for our todo_list function which contains get and post methods which accepts GET request to return a list of all items in the dictionary and a POST request to add new items to the dictionary.
Now we will be looking at how to write this program the flask RESTful way .
The flask RESTful way
from flask import Flask
from flask_restful import reqparse, abort, Api, Resource
app = Flask(__name__)
api = Api(app)
TODOS = {
'todo1': {'task': 'sleep'},
'todo2': {'task': 'eat'},
'todo3': {'task': 'code!'},
'todo4': {'task': 'repeat'},
}
def abort_if_todo_doesnt_exist(todo_id):
if todo_id not in TODOS:
abort(404, message="Todo {} doesn't exist".format(todo_id))
parser = reqparse.RequestParser()
parser.add_argument('task')
# displays a single todo item and lets you delete a todo item
class Todo(Resource):
def get(self, todo_id):
abort_if_todo_doesnt_exist(todo_id)
return TODOS[todo_id]
def delete(self, todo_id):
abort_if_todo_doesnt_exist(todo_id)
del TODOS[todo_id]
return 'You have deleted the todo item', 204
def put(self, todo_id):
args = parser.parse_args()
task = {'task': args['task']}
TODOS[todo_id] = task
return task, 201
# list of all todos, and lets you POST to add new tasks
class TodoList(Resource):
def get(self):
return TODOS
def post(self):
args = parser.parse_args()
todo_id = int(max(TODOS.keys()).lstrip('todo')) + 1
todo_id = 'todo%i' % todo_id
TODOS[todo_id] = {'task': args['task']}
return TODOS[todo_id], 201
## Setup the Api resource routing here
api.add_resource(TodoList, '/todos')
api.add_resource(Todo, '/todos/<todo_id>')
if __name__ == '__main__':
app.run(debug=True)
Here we started by making a class for each resource particular resource i.e the Todo and TodoList. In the Todo resource we define get, delete and put methods which corresponds to the GET, DELETE and PUT requests in our RESTless routes performing the same functions as their corresponding RESTless requests. The TodoList resource contains the get and post methods which also corresponds to the GET and POST requests and perform the same function as in our RESTless method . These resources are then automatically mapped by flask_restful. We then defined the resources and their corresponding urls where the endpoints can be accessed.
We can now look at result of the GET request for both methods
THE RESTful WAY
get request on 127.0.0.1:5000:/todos for RESTful
get request on 127.0.0.1:5000:/todos/ for RESTful
THE RESTless WAY
get request on 127.0.0.1:5000:/todos for RESTless
get request on 127.0.0.1:5000:/todos/ for RESTless
Looking at the results of both methods, the results are pretty much the same. However the difference is that the RESTless returns its data only in xml format which can then be converted to JSON format using the jsonify function while the RESTful automatically coverts to JSON format and support various data formats such as HTML, JSON, text etc. You can still checkout the results of the other requests.
Like the RESTless , RESTful support setting the response code and response headers using multiple return values as shown in our code above .
The flask RESTful method is built on the resource which are built on top of Flask pluggable views, giving you easy access to multiple HTTP methods just by defining methods on your resource . It has has a more organized coding structure too and therefore is the preferred option of the two methods.
CONCLUSION
- We learnt about Flask RESTless and RESTful methods of writing API's with Flask.
- We learnt about the Usefulness of both methods.
- We learnt how to write a simple API with both methods.
- We learnt why to use Flask Restful instead of Flask RESTless.
After reading this article, readers should now be able to understand both Flask API methods and select which one would be best for them over their course of API development with Flask. Next on Flask RESTful API's we shall learn how to set response headers in Flask. Thank you and see you next week ๐.