This is the first part of a three-part tutorial on creating REST APIs with Python and Flask. In this part, you will learn the basic concepts of REST architecture and the basics of creating APIs using flask. This part will also help you to get the necessary packages installed.

Here are the links to other parts of this tutorial.

  1. Part 1(You are here)
  2. Part 2
  3. Part 3

REST stands for Representational State Transfer. It is a widely accepted standard for creating stateless and reliable web services. A web service that follows the REST architecture is called a Restful service. A restful service allows seamless interoperability between different clients and servers irrespective of the language and frameworks.

If all those feels like some technical jibber-jabber, let me explain those in simple terms. The first thing you need to understand is the different types of HTTP methods. You may be familiar with the GET and POST methods but when creating REST APIs, you will need a few more. These are the HTTP methods we will be using in this tutorial.

  1. GET: This method is for requesting some data from the server. GET does not create or modify any resources in the server.
  2. POST: This method is usually used to send data to the server for performing some kind of write operations. In our REST APIs, we will be using this to create a new resource in the backend.
  3. PUT: We will use this method to update an existing resource.
  4. DELETE: You may have already guessed the purpose of this method from its name. We will be using this to delete an existing resource.

There are a few other methods also but these are the main ones.

By using a combination of these methods and a set of URLs, we can define REST operations on a resource. Consider the example of an e-commerce API. The following table shows the URLs and HTTP methods for managing products in this e-commerce website.

URL Method Action to perform
example.com/products/ POST Create a new product
example.com/products/ GET Get the list of all the products
example.com/products/product-id/ GET Get the details of a single product specified by the id
example.com/products/product-id/ PUT Update the product specified by the id
example.com/products/product-id/ DELETE Delete the product specified by the id

Enough theory. Let’s start building our REST APIs. First, we need a few things installed. Make sure you have the following

  • Python 3.8 or later installed. If you do not have Python on your computer, visit the downloads page in Python website to get Python for your operating system.
  • An HTTP client program to test our APIs. I will be using postman in this tutorial. Visit postman website if you want to get postman installed.

Installing pipenv

We will be using pipenv to create virtual environments and manage dependencies. Pipenv is a modern solution for managing dependencies in python projects. Feel free to skip this section if you already have pipenv.

To install pipenv, open a terminal(Powershell in Windows) and run the following.

For macOS and Linux:

sudo -H pip3 install pipenv

For Windows:

pip install pipenv

If you are new to pipenv, here is a quick guide to get you started.

  • To install a package using pipenv, run pipenv install package-name. This will create a new environment if not already present.
  • There will be a Pipfile and Pipfile.lock in your project directory. In Pipfile, you can see the python version required and the packages installed.
  • To start the virtual environment, run pipenv shell.
  • When you clone a project which uses pipenv, you will get Pipfile and Pipfile.lock along with other source code. You can then run pipenv install and it will create a new virtual environment and then install all the packages listed in the Pipfile.

Our First API

Before start writing our first API, we need to set up pipenv and install flask. Create a new folder profiler and open a terminal from the new directory. Run the following command to install flask using pipenv.

pipenv install flask

Once the installation finishes, create a new file app.py in the profiler directory and open it in your IDE/text editor.

Enter the following code in it and save the file.

from flask import Flask

app = Flask(__name__)


@app.route("/")
def hello():
    return "Hello, World!"

Go back to the terminal and activate pipenv by running the shell command.

pipenv shell

After activating pipenv, run the flask application.

flask run

You should see the server starting in the terminal. Open postman and send a request to http://127.0.0.1:5000/. You will see the Hello, World string in the body section.

A quick summary of what is going on:

  • app = Flask(__name__) creates a new flask application. It takes the module or package name as an argument. We used the current module name for that instead of giving our custom name.
  • app.route is a decorator. By using @app.route("/"), we are instructing flask to call the hello function whenever there is a request for the root path(/).
  • hello is just an ordinary python function returning a string. A function that handles a route is also called a view function.

Returning a string is often not very useful. In a real API, you will be returning various kinds of JSON objects. Let us update our API to return a JSON object.

Update the app.py file with the following content.

from flask import Flask, jsonify

app = Flask(__name__)


@app.route("/")
def hello():
    response = {"message": "Hello, World!"}
    return jsonify(response)

Save the file, stop the server and run it again (using flask run). Then go to postman and send another request to http://127.0.0.1:5000/. You should see the JSON object in the response instead of the string.

Here, instead of returning a string, we created a dictionary. Then we use the jsonify function from flask to convert this dictionary to a JSON response. (If you check the headers section, in postman, you can see the content type has been set to application/json.)

Converting Our Application to a Package

In the last section, we built a small application that had only one file. In a real-world application, your source code will be split into multiple files and directories. In this section, we will convert our single file application into a python package.

We will start by deleting the app.py file. Instead of it, create a new directory profiler. You will end up with one profiler directory inside another profiler directory. Now, inside the inner profiler directory, create an __init__.py file.

Your directory structure should look like this.

profiler/
    profiler/
        __init__.py
    Pipfile
    Pipfile.lock

The inner profiler directory is our application package. Whenever you run a command, you should run it from the outer profiler directory. It doesn’t matter what you name the outer directory. You can rename it to something else if you find the name confusing.

Open the profiler/__init__.py in your IDE and put the following code in it.

from flask import Flask


def create_app():
    app = Flask(__name__)

    app.config.from_pyfile("config.py", silent=True)

    @app.route("/")
    def hello():
        return {"message": "Hello, World!"}

    return app

We have made a few changes to the app we built in the last chapter.

  • There is a new create_app function that creates a flask application and returns it. Flask requires either an app variable or a create_app function to automatically load the app.
  • There is an additional line app.config.from_pyfile("config.py", silent=True). Here, we are instructing flask to load our configuration from the config.py file. We might not always have this file. We do not want flask to throw an error if the file is not found. That is why we set silent=True.
  • We moved the hello function to the create_app function because app variable is not available outside this function.
  • You might also notice that we have removed the jsonify function and returned a dictionary directly. Flask is smart enough to automatically convert dictionaries to JSON but it would not convert other python types. It is always a good idea to use jsonify if you are not sure of the data type.

If you go ahead and try to run the application using flask run, it will throw an error. When using the run command, Flask will look for an app variable or a create_app function inside the app.py file. Since we no longer have the app.py file, we have to manually tell flask about the module or package which contains the app object.

We do this by creating an environment variable called FLASK_APP. You can manually set this from your terminal but you will have to set it again every time you open a new terminal. Also, Windows and Unix handle environment variables differently. To avoid these pitfalls, we will use pipenv to load the environment variables for us.

Create a new file named .env in the outer profiler directory and put the following contents in it.

FLASK_APP=profiler
FLASK_DEBUG=1
  • The first line FLASK_APP=profiler sets profiler as the application package. Flask will now look for an app object in this package.
  • FLASK_DEBUG=1 turns on flask’s debug mode. In debug mode, we do not have to restart the server every time we make a change in python files. Debug mode also gives us descriptive error messages and full traceback when an error occurs. You should never turn on debug mode in production environments.

You need to restart the pipenv shell to load new environment variables. If you are already in a pipenv shell, quit the shell by typing exit and reactivate the shell using pipenv shell.

Once you are inside the shell, start the development server using flask run. You should see the server running.

Load the server URL http://127.0.0.1:5000/ in postman. You will see the JSON object in the response body.

Adding A Configuration File

Usually, an application has several configuration values such as session keys, database credentials, file upload paths etc. Let us create a configuration file to store our application’s configurations.

Create a new config.py file in the inner profiler directory. Your directory structure should look like this now.

profiler/
    profiler/
        __init__.py
        config.py
    .evn
    Pipfile
    Pipfile.lock

Put the following in this new file.

SECRET_KEY = "change-this-please"

Now, let us verify the configurations are correctly loaded by using this configuration variable in our API. Update the return statement of our hello function with the following line.

return {"message": "Hello, World!", "secret_key": app.config["SECRET_KEY"]}

Adding a new configuration file requires restarting the server. Kill the development server if it is already running and restart it again. Load the API again in postman and you should see the following response.

{
  "message": "Hello, World!",
  "secret_key": "change-this-please"
}

You need to restart the server whenever you add a new configuration file. Adding a new configuration variable to an already existing file does not require a server restart.

Continue to part 2 of this tutorial


Last updated on February 5, 2022
Tags: Python Flask Tutorial