JoinDownload
This is drafted post. Please setdraft: falsein this .mdx file once ready to be published.

Create Your First REST API with Python and Flask

6 Min Read

Henry Boisdequin

In this tutorial, I will be covering what a REST API is and how to build one with Python and Flask. Before diving in, here are the prerequisites to this tutorial: basic knowledge of Python, HTTP, and asynchronous programming. Here is a great course to learn about basic Python, HTTP, and asynchronous programming. Also, you will need to have Python installed and a code editor/IDE of choice (I'm using VSCode). With that out of the way, let's dive right into it!

What is a REST API?

REST is short for Representational State Transfer, which basically means that REST is a way of building HTTP services. These services can create, read, update, and delete data (CRUD operations).

A REST API is located on an endpoint URL. An endpoint URL is any URL, for example, https://www.google.com/, or http://localhost:3000/. To use a REST API, you send a request to the endpoint URL, specifying what CRUD operation you want to perform. To specify which CRUD operation you want to perform, you specify a path in which the operation takes place. For example, http://localhost:3000/users would return all the users in the database.

Let's have a look at the different CRUD operations.

NameCRUDAction
GETreadreturns data
POSTcreatecreates data
PUT/PATCHupdate/modifyupdates/modifies data
DELETEdeletedeletes data

As you can see, each of the CRUD operations performs certain services on the endpoint URL. Each of these operations returns JSON or JavaScript Object Notation which is a lightweight format for storing and transporting data. Let's have a look at an example:

{
"users": [
{ "firstName": "Harry", "lastName": "Jones" },
{ "firstName": "Anna", "lastName": "Davis" },
{ "firstName": "Peter", "lastName": "Williams" }
]
}

The JSON above stores a list of users, each with a first and last name.

Also, I would like to point out that even on the same endpoint, we could have different CRUD operations. Let's have a look at an example:

GET /authors
POST /authors
GET /authors/1
PUT /authors/1
PATCH /authors/1
DELETE /authors/1
GET /authors/1/posts
GET /authors/1/posts/1
DELETE /authors/1/posts/1

As you can see, we have different CRUD operations on the same endpoints. This is allowed as each of these methods are distinguished with different CRUD operations. If our endpoint URL was http://localhost:3000, I would do a POST to authors by running this command: curl -X POST 'name=Test Author' http://localhost:3000/authors. This command specifies the operation, the name of the author, and the final path (where the operation is). If I wanted to do a GET on the same path, I would run this command: curl -X GET http://localhost:3000/authors which would trigger the GET command.

Now that we know what a REST API is, let's make a plan on what our first REST API is going to look like.

Planning Our First REST API

Our REST API will be a database for books. Our REST API will be able to add, update, and delete books from our database. We will store the books name, author, and id in our database. Let's have a look at the CRUD operations we will implement.

Firstly, we need a way to return all the books in our database. For that, we would use the GET method which returns data. The path for this operation will be "/books" which when called will return all the books in our database. Next, we need a way to create and add new books to our database. We can do that with the PUT method which allows us to update/create data. The path for this operation will be "/book:id". The ":id" specifies that an id will need to be passed into the path. To get an individual book, we will again use the GET method. The path for this method will be "/book:id". The ":id" specifies that an id will need to be passed into the path. You might be thinking how do we have two different requests on the same endpoint: "/book:id"? If the requests are different (GET, PUT) then it is allowed to have one endpoint for both of them. To delete a book we will also use the "/book:id" but with the DELETE request.

Here is the summarized version:

"/books" - GET - Get all the books in the database
"/book:id" - GET - Get a certain book by specifying the book id
"/book:id" - PUT - Create a book with the id given
"/book:id" - DELETE - Delete the book with the id given

Coding our First REST API

Let's get right into coding! First, let's make a folder:

mkdir book-rest-api/

Open the folder in your code editor of choice. Next, we need to install Flask. Run pip3 install flask-restful in your project directory. Let's get a "Hello World" program running.

Create a new file called api.py and put the following code inside:

from flask import Flask
from flask_restful import Resource, Api
app = Flask(__name__)
api = Api(app)
class HelloWorld(Resource):
def get(self):
return {"hello": "world"}
api.add_resource(HelloWorld, "/")
if __name__ == "__main__":
app.run(debug=True)

In this code, we are creating a new RESTful Flask app and at the endpoint "/" we are returning a JSON object with the key "hello" equal to "world". Then, we are running the app if this file is running as the main module, further reading on if __name__ == "__main__" can be found here. Let's run our code by running python3 api.py. The output will look something like this:

* Serving Flask app "api" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: on
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: 519-646-976

Click on the endpoint URL (http://127.0.0.1:5000/) and once you open it up you should see a JSON object:

{
"hello": "world"
}

Nice, our "hello world" example works! If you look into the terminal again. You can see a message somewhat like this:

127.0.0.1 - - [11/Jan/2021 08:14:52] "GET / HTTP/1.1" 200 -

As you can see we are making a GET request to out endpoint URL with a 200 response. A 200 response means that everything worked. You may have heard of other HTTP status codes like 404 (not found) or 400 (bad request). If you go to "/hello" on our endpoint URL, you can see that we get a 404 message in the terminal because that path doesn't exist. Now let's start converting this example into a database for books. Replace you're old api.py code with this:

from flask import Flask, request
from flask_restful import Resource, Api
app = Flask(__name__)
api = Api(app)
books = {}
class Book(Resource):
def get(self, book_id):
return {book_id: books[book_id]}
def put(self, book_id):
books[book_id] = request.form["name"]
return {book_id: books[book_id]}
api.add_resource(Book, '/<string:book_id>')
if __name__ == '__main__':
app.run(debug=True)

As you can see we have implemented the get and put method. In the get method, we are returning the book with the given book id. In the put method, we are requesting that data is sent back to us. We are storing that data inside the books map with the specified id and returning the book we just created. When we add the resource to our API, we are specifying the path as "/<string:book_id>". That means that we are requesting for a todo id in our endpoint URL. Let's test our API out with the Python requests module. Run pip3 install requests. Let's create a new file called test_api.py. Our test_api.py should look like this:

from requests import put, get
put('http://127.0.0.1:5000/book1', data={'name': 'The Slight Edge'}).json()
print(get('http://127.0.0.1:5000/book1').json())

In this file, we are making a PUT request to our API, specifying that we want to make a new book with an id of 1, name of "The Slight Edge". In the next line, we are making a GET request to our API, requesting for the data we have just made. Let's run test_api.py by running this command: python3 test_api.py. As you can see we get this response back:

{'book1': 'The Slight Edge'}

It's working! Now if we go to our browser, and go to "/book1" we get some JSON back:

{
"book1": "The Slight Edge"
}

Perfect, everything is working. Now, we need to implement a DELETE method. We can do this by creating a new function in our Book class.

Class Book

class Book(Resource):
def get(self, book_id):
return {book_id: books[book_id]}
def put(self, book_id):
books[book_id] = request.form["name"]
return {book_id: books[book_id]}
def delete(self, book_id):
del books[book_id]
return "", 204

As you can see, in the delete method, we are just deleting the book in the books map with the id given. In the delete method, we are also returning a 204 response which means it was successful. Let's test this out!

test_api.py

from requests import put, get, delete
# put('http://127.0.0.1:5000/book1', data={'name': 'The Slight Edge'}).json()
# print(get('http://localhost:5000/book1').json())
print(delete('http://127.0.0.1:5000/book1'))

Let's run our code: python3 test_api.py. We should get a 204 response which means it was successful! Now, we should cover the case when we receive a book id which doesn't exist. For this, let's create a function called abort_if_book_doesnt_exist. Our api.py should now look like this:

from flask import Flask, request
from flask_restful import Resource, Api, abort
app = Flask(__name__)
api = Api(app)
books = {}
def abort_if_book_doesnt_exist(book_id):
if book_id not in books:
abort(404, message=f"Book {book_id} doesn't exist.")
class Book(Resource):
def get(self, book_id):
abort_if_book_doesnt_exist(book_id)
return {book_id: books[book_id]}
def put(self, book_id):
books[book_id] = request.form["name"]
return {book_id: books[book_id]}, 201
def delete(self, book_id):
abort_if_book_doesnt_exist(book_id)
del books[book_id]
return "", 204
api.add_resource(Book, '/<string:book_id>')
if __name__ == '__main__':
app.run(debug=True)

Our abort_if_book_doesnt_exist function aborts the request if the book id doesn't exist and returns a 404 not found. In the delete and get methods, we call the abort_if_book_doesnt_exist function to make sure that we are getting or deleting a valid book. Now, if we go to our browser and go to "/book1" (that we just deleted) we now see some JSON instead of a Flask error:

{
"message": "Book book1 doesn't exist."
}

Nice, it works! Now, let's go over argument parsing with flask. Argument parsing allows us to pass arguments to our URL. Let's implement this in our put method:

api.py

from flask import Flask, request
from flask_restful import Resource, Api, abort, reqparse
app = Flask(__name__)
api = Api(app)
books = {}
def abort_if_book_doesnt_exist(book_id):
if book_id not in books:
abort(404, message=f"Book {book_id} doesn't exist.")
parser = reqparse.RequestParser()
parser.add_argument("name")
parser.add_argument("author")
class Book(Resource):
def get(self, book_id):
abort_if_book_doesnt_exist(book_id)
return {book_id: books[book_id]}
def put(self, book_id):
args = parser.parse_args()
book = {"name": args["name"], "author": args["author"]}
books[book_id] = book
return book, 201
def delete(self, book_id):
abort_if_book_doesnt_exist(book_id)
del books[book_id]
return "", 204
api.add_resource(Book, '/<string:book_id>')
if __name__ == '__main__':
app.run(debug=True)

As you can see, in the put method, we are getting the name and author of the book from the arguments. Let's test this out. Now open up your API Campsite of choice. Mine is Firecamp which you can download here: https://firecamp.io/. Now set up a new PUT request with 2 parameters a name and author (example: http://127.0.0.1:5000/book1?name=The%20Slight%20Edge&author=Jeff%20Olson). This is how to do it in Firecamp:

Firecamp Example

Just click a new HTTP tab, as the URL type http://127.0.0.1:5000/book1 and add two parameters: name and author. Fill in the values and like magic, we get our JSON back:

{
"name": "The Slight Edge",
"author": "Jeff Olson"
}

Now if we go to the browser and go to http://127.0.0.1:5000/book1, we see the same JSON! Looks like our argument parsing is working. The final thing we need to do is to be able to have a request which sends back all of our books. We can do that by creating a new Books class.

api.py

from flask import Flask, request
from flask_restful import Resource, Api, abort, reqparse
app = Flask(__name__)
api = Api(app)
books = {}
def abort_if_book_doesnt_exist(book_id):
if book_id not in books:
abort(404, message=f"Book {book_id} doesn't exist.")
parser = reqparse.RequestParser()
parser.add_argument("name")
parser.add_argument("author")
class Book(Resource):
def get(self, book_id):
abort_if_book_doesnt_exist(book_id)
return {book_id: books[book_id]}
def put(self, book_id):
args = parser.parse_args()
book = {"name": args["name"], "author": args["author"]}
books[book_id] = book
return book, 201
def delete(self, book_id):
abort_if_book_doesnt_exist(book_id)
del books[book_id]
return "", 204
class Books(Resource):
def get(self):
return books
api.add_resource(Book, '/<string:book_id>')
api.add_resource(Books, '/books')
if __name__ == '__main__':
app.run(debug=True)

In this code, we are just creating a new class which has a get method. This method returns our map of books. Once we register our resource, when we go to "/books" we see all our books! Awesome! We have finished our first REST API!


Thanks for following this tutorial on how to create your first REST API with Flask and Python. All the code is on GitHub. Thanks for reading!

Henry

CONTENT
What is a REST API?Planning Our First REST APICoding our First REST API

Links

DownloadDocChange LogsCookiesTerms & ConditionsPrivacy PolicyContact Us

Apps & Integrations

HTTPGraphQLWebsocketSocketIO

Firecamp Newsletter