How to make a RESTful API with flask-Restplus and Python

I like Flask. According to their official documentation website:

Flask is a microframework for Python based on Werkzeug, Jinja 2 and good intentions. And before you ask: It's BSD licensed!

In past few years REST (REpresentational State Transfer) has emerged as a go-to design for making APIs.

In case you don't know what APIs are, watch this https://www.youtube.com/watch?v=s7wmiS2mSXY

There are already much written about how to make APis in Flask. Today, I am going to show how to make an API using Flask and rest-plus.

Rest-plus, as per their website:

Flask-RESTPlus is an extension for Flask that adds support for quickly building REST APIs. Flask-RESTPlus encourages best practices with minimal setup. It provides a coherent collection of decorators and tools to describe your API and expose its documentation properly (using Swagger).

Well, let's jump right in.

Before installation, I'd advise to set up a virtualenv environment.

Installation

$ pip install flask flask-restplus

Basics

from flask import Flask
from flask_restplus import API, Resource, fields

app = Flask(__name__)
api = Api(app, version='1.0', title='Book Api', description='An Api for Books')

These are the formalities. Now, let's make a toy data structure, upon which we will perform our REST operations.

all_books = [
    {
        "name": "White Tiger",
        "author": "Arvind Adiga"
    },
    {
        "name": "Animal Farm",
        "author": "George Orwell"
    }
]

Every entry of a book must have some structure, some model. At least containing the name of the book and its author.

book_model = api.model("book", {
    "name": fields.String("Name of the book."),
    "author": fields.String("Name of the author.")
})

This will give us some structure while making a POST operation as well.

Now, time to write some real code.

@api.route('/books')
class AllBooks(Resource):

    @api.marshal_with(book_model, envelope='data')
    def get(self):
        """
        get all the books
        """
        return all_books, 200

    @api.expect(book_model)
    def post(self):
        """
        add new book to the list
        """
        new_book = api.payload
        all_books.append(new_book)
        return {'result': 'Book added'}, 201

if __name__ == '__main__':
    app.run(debug=True)

Yes, it's just that easy. Just write a get function for a route and it'll get all the GET HTTP routes on it.

$ python book_api.py

It'll give this output in the terminal

 * Debugger is active!
 * Debugger PIN: 340-024-644
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
  • Debugger is active! Don't forget to turn this off when you take your project in Production.

You can either use Curl to perform your HTTP operations or use the Swagger UI, which you'll see when you visit the http://127.0.0.1:5000/

Or use any other tool of your choice, I prefer swagger since it automatically generates a documentation.

Let's make a GET request.

cap_01.PNG

$ curl -X GET --header 'Accept: application/json' 'http://localhost:5000/books'

{
  "data": [
    {
      "name": "White Tiger",
      "author": "Arvind Adiga"
    },
    {
      "name": "Animal Farm",
      "author": "George Orwell"
    }
  ]
}

cap_02.PNG

Let's make a POST request

curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' -d '{ \
   "name": "1984", \
   "author": "George Orwell" \
 }' 'http://localhost:5000/books'

{
  "result": "Book added"
}

Now, let's write another piece of code to display all books by the specified author.

@api.route('/author/<author>')
class Author(Resource):

    @api.marshal_with(book_model, envelope='data')
    def get(self, author):
        """
        get all theb ooks by the author
        """
        result = [book for book in all_books if book['author'] == author]
        return result

The variable author here is directly taken from the URL path. Now, let's test this out. And since our data is not stored in a database, you'll have to make the post statement again.

Because when the flask server restarts all the temporarily stored data is reset.

Let's get the books written by George Orwell.

$ curl -X GET --header 'Accept: application/json' 'http://localhost:5000/author/George%20Orwell'

{
  "data": [
    {
      "name": "Animal Farm",
      "author": "George Orwell"
    },
    {
      "name": "1984",
      "author": "George Orwell"
    }
  ]
}

Now, let's look at the piece of code that shows the PUT and DELETE HTTP requests.

If you notice the code below, you'll see, I am using a parser. That is to get the new name of the author for that book.

parser = api.parser()
parser.add_argument('author', type=str, required=True, help='name of the author', location='form')

@api.route('/book/<book_name>')
class Book(Resource):

    def get(self, book_name):
        """
        get details of particular book
        """
        result = [book for book in all_books if book['name'] == book_name]
        return result

    @api.doc(parser=parser)
    def put(self, book_name):
        """
        Change the book details
        """
        args = parser.parse_args()
        for index, book in enumerate(all_books):
            if book['name'] == book_name:
                all_books[index]['author'] = args['author']
                return book, 201
        return None, 201

    def delete(self, book_name):
        """
        delete the book
        """
        for index, book in enumerate(all_books):
            if book['name'] == book_name:
                del all_books[index]
                return {"response": "book deleted"}, 204
        return None, 404

Now, let's try to change the Author for 1984.

$ curl -X PUT --header 'Content-Type: multipart/form-data' --header 'Accept: application/json' -F author=Anshul%20Chauhan  'http://localhost:5000/book/1984'

{
  "name": "1984",
  "author": "Anshul Chauhan"
}

But, you all know, Anshul didn't write this book. So, now let's delete this entry.

$ curl -X DELETE --header 'Accept: application/json' 'http://localhost:5000/book/1984'

That's it.

You can find the entire code at one place here https://gist.github.com/anshulc95/b2e3c67af54345c293f8e5dd3a2cbb7a

In case, you're not sure which HTTP response code to use, have a look this documentation https://developer.mozilla.org/en-US/docs/Web/HTTP/Status

If you want to make the API purely with Flask, then check this tutorial by the creator of Flask itself.

Contact me at twitter.com/anshulc95, and check my projects at http://github.com/anshulc95/.

Now go and make stuff. Keep coding.

Related article

AUTHOR

READ NEXT

Boostlog is an online community for developers
who want to share ideas and grow each other.

Delete an article

Deleted articles are gone forever. Are you sure?