2013-07-28T19:11:38Z

Designing a RESTful API using Flask-RESTful

This is the third article in which I explore different aspects of writing RESTful APIs using the Flask microframework. Here is the first, and the second.

The example RESTful server I wrote before used only Flask as a dependency. Today I will show you how to write the same server using Flask-RESTful, a Flask extension that simplifies the creation of APIs.

The RESTful server

As a reminder, here is the definition of the ToDo List web service that has been serving as an example in my RESTful articles:

HTTP MethodURIAction
GEThttp://[hostname]/todo/api/v1.0/tasksRetrieve list of tasks
GEThttp://[hostname]/todo/api/v1.0/tasks/[task_id]Retrieve a task
POSThttp://[hostname]/todo/api/v1.0/tasksCreate a new task
PUThttp://[hostname]/todo/api/v1.0/tasks/[task_id]Update an existing task
DELETEhttp://[hostname]/todo/api/v1.0/tasks/[task_id]Delete a task

The only resource exposed by this service is a "task", which has the following data fields:

  • uri: unique URI for the task. String type.
  • title: short task description. String type.
  • description: long task description. Text type.
  • done: task completion state. Boolean type.

Routing

In my first RESTful server example (source code here) I have used regular Flask view functions to define all the routes.

Flask-RESTful provides a Resource base class that can define the routing for one or more HTTP methods for a given URL. For example, to define a User resource with GET, PUT and DELETE methods you would write:

from flask import Flask
from flask_restful import Api, Resource

app = Flask(__name__)
api = Api(app)

class UserAPI(Resource):
    def get(self, id):
        pass

    def put(self, id):
        pass

    def delete(self, id):
        pass

api.add_resource(UserAPI, '/users/<int:id>', endpoint = 'user')

The add_resource function registers the routes with the framework using the given endpoint. If an endpoint isn't given then Flask-RESTful generates one for you from the class name, but since sometimes the endpoint is needed for functions such as url_for I prefer to make it explicit.

My ToDo API defines two URLs: /todo/api/v1.0/tasks for the list of tasks, and /todo/api/v1.0/tasks/<int:id> for an individual task. Since Flask-RESTful's Resource class can wrap a single URL this server will need two resources:

class TaskListAPI(Resource):
    def get(self):
        pass

    def post(self):
        pass

class TaskAPI(Resource):
    def get(self, id):
        pass

    def put(self, id):
        pass

    def delete(self, id):
        pass

api.add_resource(TaskListAPI, '/todo/api/v1.0/tasks', endpoint = 'tasks')
api.add_resource(TaskAPI, '/todo/api/v1.0/tasks/<int:id>', endpoint = 'task')

Note that while the method views of TaskListAPI receive no arguments the ones in TaskAPI all receive the id, as specified in the URL under which the resource is registered.

Request Parsing and Validation

When I implemented this server in the previous article I did my own validation of the request data. For example, look at how long the PUT handler is in that version:

@app.route('/todo/api/v1.0/tasks/<int:task_id>', methods = ['PUT'])
@auth.login_required
def update_task(task_id):
    task = filter(lambda t: t['id'] == task_id, tasks)
    if len(task) == 0:
        abort(404)
    if not request.json:
        abort(400)
    if 'title' in request.json and type(request.json['title']) != unicode:
        abort(400)
    if 'description' in request.json and type(request.json['description']) is not unicode:
        abort(400)
    if 'done' in request.json and type(request.json['done']) is not bool:
        abort(400)
    task[0]['title'] = request.json.get('title', task[0]['title'])
    task[0]['description'] = request.json.get('description', task[0]['description'])
    task[0]['done'] = request.json.get('done', task[0]['done'])
    return jsonify( { 'task': make_public_task(task[0]) } )

Here I have to make sure the data given with the request is valid before using it, and that makes the function pretty long.

Flask-RESTful provides a much better way to handle this with the RequestParser class. This class works in a similar way as argparse for command line arguments.

First, for each resource I define the arguments and how to validate them:

from flask_restful import reqparse

class TaskListAPI(Resource):
    def __init__(self):
        self.reqparse = reqparse.RequestParser()
        self.reqparse.add_argument('title', type = str, required = True,
            help = 'No task title provided', location = 'json')
        self.reqparse.add_argument('description', type = str, default = "", location = 'json')
        super(TaskListAPI, self).__init__()

    # ...

class TaskAPI(Resource):
    def __init__(self):
        self.reqparse = reqparse.RequestParser()
        self.reqparse.add_argument('title', type = str, location = 'json')
        self.reqparse.add_argument('description', type = str, location = 'json')
        self.reqparse.add_argument('done', type = bool, location = 'json')
        super(TaskAPI, self).__init__()

    # ...

In the TaskListAPI resource the POST method is the only one the receives arguments. The title argument is required here, so I included an error message that Flask-RESTful will send as a response to the client when the field is missing. The description field is optional, and when it is missing a default value of an empty string will be used. One interesting aspect of the RequestParser class is that by default it looks for fields in request.values, so the location optional argument must be set to indicate that the fields are coming in request.json.

The request parser for the TaskAPI is constructed in a similar way, but has a few differences. In this case it is the PUT method that will need to parse arguments, and for this method all the arguments are optional, including the done field that was not part of the request in the other resource.

Now that the request parsers are initialized, parsing and validating a request is pretty easy. For example, note how much simpler the TaskAPI.put() method becomes:

    def put(self, id):
        task = filter(lambda t: t['id'] == id, tasks)
        if len(task) == 0:
            abort(404)
        task = task[0]
        args = self.reqparse.parse_args()
        for k, v in args.iteritems():
            if v != None:
                task[k] = v
        return jsonify( { 'task': make_public_task(task) } )

A side benefit of letting Flask-RESTful do the validation is that now there is no need to have a handler for the bad request code 400 error, this is all taken care of by the extension.

Generating Responses

My original REST server generates the responses using Flask's jsonify helper function. Flask-RESTful automatically handles the conversion to JSON, so instead of this:

        return jsonify( { 'task': make_public_task(task) } )

I can do this:

        return { 'task': make_public_task(task) }

Flask-RESTful also supports passing a custom status code back when necessary:

        return { 'task': make_public_task(task) }, 201

But there is more. The make_public_task wrapper from the original server converted a task from its internal representation to the external representation that clients expected. The conversion included removing the id field and adding a uri field in its place. Flask-RESTful provides a helper function to do this in a much more elegant way that not only generates the uri but also does type conversion on the remaining fields:

from flask_restful import fields, marshal

task_fields = {
    'title': fields.String,
    'description': fields.String,
    'done': fields.Boolean,
    'uri': fields.Url('task')
}

class TaskAPI(Resource):
    # ...

    def put(self, id):
        # ...
        return { 'task': marshal(task, task_fields) }

The task_fields structure serves as a template for the marshal function. The fields.Url type is a special type that generates a URL. The argument it takes is the endpoint (recall that I have used explicit endpoints when I registered the resources specifically so that I can refer to them when needed).

Authentication

The routes in the REST server are all protected with HTTP basic authentication. In the original server the protection was added using the decorator provided by the Flask-HTTPAuth extension.

Since the Resouce class inherits from Flask's MethodView, it is possible to attach decorators to the methods by defining a decorators class variable:

from flask_httpauth import HTTPBasicAuth
# ...
auth = HTTPBasicAuth()
# ...

class TaskAPI(Resource):
    decorators = [auth.login_required]
    # ...

class TaskAPI(Resource):
    decorators = [auth.login_required]
    # ...

Conclusion

The complete server implementation based on Flask-RESTful is available in my REST-tutorial project on github. The file with the Flask-RESTful server is rest-server-v2.py.

You can also download the entire project including both server implementations and a javascript client to test it:

Download REST-tutorial project.

Let me know if you have any questions in the comments below.

Miguel

188 comments

  • #101 Miguel Grinberg said 2015-03-06T19:53:17Z

    @Jia: remove the location='json' argument from your add_argument calls and I think it'll work fine.

  • #102 Manuel said 2015-03-16T03:46:04Z

    Hi Miguel,

    I have a question regarding mixing up views.py (from your megatutorial) and a Flask-RESTful. I implemented a login function and I am saving the user in g.user. Since this user is already authenticated, I'd like to pull information related to this user through the API. For example, the user places online orders and I save them in the database. Then I want to pull this information from the user and I'd like to do it through the API. However, I cannot import g.user in api.py since flask will through an error. The semi solution I have right now is that my front end calls a function in views.py, I pull the user id information here and then I do a post request to the API from here. This is obviously very messy. How would you use the global user cached variable in this case?

    I apologize for the open ended question, but your extensive knowledge in Flask would be very helpful.

    Regards,

  • #103 Miguel Grinberg said 2015-03-17T03:34:14Z

    @Manuel: not sure I understand. You can't import g.user, but you can import g and then reference g.user. As long as you are responding to a request g should have all the data.

  • #104 Manuel Godoy said 2015-03-18T01:31:45Z

    Miguel,

    I think I did not explain myself properly. What I meant to say was that in my views.py I am importing g and I am storing current_user in g.user when the user is authenticated. However, when I make a call to my api, which is in a separate file called api.py, directly from the front end, I need to get g.user to extract the proper data from my database. The only solution I've found is to have the front end request the function in views.py, get the user id, and then inside this function I am doing the api POST. See below my partial code:

    @login_required @app.route('/check_retrieve' , methods = ['GET', 'POST']) def check_retrieve(): app_url = request.url_root usr_id = g.user.key().id() d = {'id': str(usr_id)} r = requests.get(urljoin(app_url,"/_ah/api/myapp/v1/orderGET"), params = d) return (r.text, r.status_code, r.headers.items())

    Thank you for your feedback.

    Cheers,

  • #105 Miguel Grinberg said 2015-03-18T02:49:46Z

    @Manuel: Sorry yes, I did not understand. What you need to do is add authentication to your API (if you don't have that yet). The front end can use the same login info that it used to authenticate against the web application and send it to the API. That way your API is decoupled from the web application.

  • #106 malamoney said 2015-03-18T17:49:27Z

    With flask-restful how would you handle non-CRUD type actions? For example, let say I had a "player" resource and I wanted to perform a "trade" action on that player. In code you would likely have something like:

    playerInstance.trade( newTeamId )

    I'm torn with the idea of creating a "trade" resource and create a new trade. I "think" that's the most REST way to do it, but it's not very intuitive or user friendly. Plus it seems that flask-restful only supports the basic HTTP verbs as endpoint functions.

    Thoughts.

  • #107 Miguel Grinberg said 2015-03-19T04:48:34Z

    @malamoney: well, non-CRUD actions are not REST. If you want to build a non REST API then you can use native flask and create the routes that you like. I would actually go with the trade is a resource option that you mentioned, that is the best design in my opinion.

  • #108 John said 2015-03-20T09:19:09Z

    I have a quick question, so you say "Flask extension that simplifies the creation of APIs." So would you suggest to use Flask-rest over just flask for most/all api creations?

  • #109 Miguel Grinberg said 2015-03-21T21:54:00Z

    @John: Each developer is different, so I really can't recommend that use of Flask-RESTful over plain Flask for every case. It mostly depends on personal taste. I find Flask alone sufficient for my own API needs.

  • #110 danfromisrael said 2015-04-12T14:43:28Z

    hi miguel and thank a lot for this post! it's very helpful and i've used it to build my first python hello world app :-)

    i've kinda wondered how can I use the 'api.add_resource()' method without using the flask-restful api Resource Class default constructor (for dependency injection purposes).

    i've looked everywhere and eventually came up with this solution: http://stackoverflow.com/a/29590531/303114

    i'd love to hear your opinion since there might be an easier way to accomplish that. (i havent found anything on the web besides that)

  • #111 Miguel Grinberg said 2015-04-16T04:29:33Z

    @danfromisrael: That's the only approach that doesn't require changes to Flask-RESTful. If you want something a bit more involved, then you could look at creating a subclass of Flask-RESTful's Api class where you overload method add_resource() to also take arguments to send to the resource class. Passing the args to the class constructor is easy, because Flask's as_view() method is used here, and this method passes any extra arguments it receives after its own to the constructor.

  • #112 Neelabh Singh said 2015-06-30T04:37:03Z

    Thanks for such a nice tutorial. I just want to asked few thing In blog http://blog.miguelgrinberg.com/post/designing-a-restful-api-using-flask-restful if run like http://127.0.0.1:5000/index.html instead of http://localhost:5000/index.html Then This program will not run. Second Why index.html is put inside static but not template folder.

  • #113 Miguel Grinberg said 2015-07-01T19:04:00Z

    localhost and 127.0.0.1 are the same thing, it should work. The index.html goes in static just because for this application there are no template elements in it, it's really a static file.

  • #114 Manuel Godoy said 2015-07-02T05:12:17Z

    Dear Miguel,

    Are you planning on combining Flask-Marshmallow with Flask-Restful in your blog? A lot of users, specially in Reddit, seem to love the former library, but there is not a good tutorial for this combo. You have us all spoiled with your tutorials :).

    Thank you,

  • #115 Shayan Arman said 2015-09-16T21:34:05Z

    Is flask-restful still fairly similar in 2015? Or do you think that another tutorial should be used?

  • #116 Miguel Grinberg said 2015-09-16T23:11:20Z

    @Shayan: I believe there are no major changes in the current version from when I did this.

  • #117 Mitul said 2015-10-22T15:41:53Z

    hi Miguel, how authenticate work with http://docs.stormpath.com/flask/quickstart/ for restful api? I'm getting the response from stormpath true and false, how do I check authentication everytime when any put, post, get method called ?

    auth_attempt = application.authenticate_account( 'username', 'password') g.user = auth_attempt

    I'm getting the response in g.user correctly. pls suggest ??

  • #118 Miguel Grinberg said 2015-10-22T18:21:16Z

    @Mitul: I have not used stormpath myself, so I'm not familiar with their service, but I would imagine if the client is sending username and password with every request you can just authenticate all the requests. Alternatively, you can generate a token when a client authenticates, then in subsequent requests just validate the token, which is faster, as it eliminates stormpath in all requests except the first.

  • #119 bryan said 2015-12-03T21:16:40Z

    Thanks for all your great work in showing how to use Flask - some of the clearest tutorials I've ever seen. I've been reading and doing some of the mega-tutorial and RESTful API tutorials. I am currently trying to find a suitable architecture for this objective: a RESTful API (in python) with data coming from an Oracle database (GET only). I'm currently looking at Flask, and this tutorial looks like a step in the right direction. I'm wondering if I should try to connect to Oracle directly or use SQLAlchemy as you did in the mega-tutorial. Since these calls will be GET only, I doubt I need SQLAlchemy, but would it make life easier? Any suggestions you have are welcome. thanks, brian

  • #120 Miguel Grinberg said 2015-12-10T17:47:10Z

    @bryan: you can use SQLAlchemy without the ORM if that seems overkill for your project. That is going to be nicer than working with the database directly.

  • #121 Rishi said 2015-12-29T07:30:27Z

    How do you do token based authentication with Flask-Restful like you did in the RESTful Authentication tutorial?

  • #122 Miguel Grinberg said 2016-01-03T22:43:51Z

    @Rishi: you just need to apply the decorator in the proper way for Method View routes, but other than that there is no difference.

  • #123 Zeke said 2016-05-29T19:42:11Z

    Awesome article! How about going a little further and including a database for the service?

  • #124 Okengroth said 2016-06-01T14:12:25Z

    Miguel, the example is great. Just one question: i've been building an API-REST with the previous approach. Should I start using flask-restful or keep the previous approach? Thanks!

  • #125 Miguel Grinberg said 2016-06-02T02:31:59Z

    @Okengroth: that's really up to you, both approaches are decent. I do not use Flask-RESTful myself, my preference is to write the endpoints as straight Flask route functions.

Leave a Comment