Python REST APIs With Flask, Connexion, and SQLAlchemy – Part 1 (original) (raw)

Most modern web applications are powered by a REST API under the hood. That way, developers can separate the front-end code from the back-end logic, and users can interact with the interface dynamically. In this three-part tutorial series, you’ll build a REST API with the Flask web framework.

You’ll create a foundation with a basic Flask project then add endpoints and connect them to a SQLite database. You’ll test your API with Swagger UI API documentation that you’ll build along the way.

In the first part of this tutorial series, you’ll learn how to:

After finishing the first part of this series, you’ll move on to the second part, where you’ll learn to use a proper database to store your data permanently instead of relying on in-memory storage.

This tutorial series is a hand-on guide on how to create a REST API with Flask and interact with it using CRUD operations. If you want to refresh your knowledge on working with APIs, then you can give Python and REST APIs: Interacting With Web Services a read.

You can download the code for the first part of this project by clicking the link below:

Demo

In this three-part tutorial series, you’ll build a REST API to keep track of notes for people that may visit you throughout the year. In this tutorial, you’ll create people like the Tooth Fairy, the Easter Bunny, and Knecht Ruprecht.

Ideally, you want to be on good terms with all three of them. That’s why you’ll send them notes, to increase the chance of getting valuable gifts from them.

You can interact with your application by leveraging the API documentation. Along the way, you’ll build a basic front end that reflects the contents of your database:

In the first part of this series, you’ll create a base Flask project and plug in your first API endpoints. At the end of this part, you’ll be able to see a list of people in the front end and manage each person in the back end:

By leveraging Swagger UI, you’ll create handy documentation for your API along the way. That way, you’ll have the opportunity to test how your API works at each stage of this tutorial and get a useful overview of all your endpoints.

Planning Part One

Besides building the Flask project foundation, you’re going to create a REST API that provides access to a collection of people and to the individuals within that collection. Here’s the API design for the people collection:

Action HTTP Verb URL Path Description
Read GET /api/people Read a collection of people.
Create POST /api/people Create a new person.
Read GET /api/people/ Read a particular person.
Update PUT /api/people/ Update an existing person.
Delete DELETE /api/people/ Delete an existing person.

The REST API that you’ll be building will serve a simple people data structure where the people are keyed to the last name, and any updates are marked with a new timestamp.

The dataset that you’ll be working with looks like this:

One of the purposes of an API is to decouple the data from the application that uses it, thereby hiding the data implementation details. Later in this tutorial series, you’ll save your data in a database. But for the start, an in-memory data structure works fine.

Getting Started

In this section, you’ll prepare the development environment for your Flask REST API project. First, you’ll create a virtual environment and install all the dependencies that you need for your project.

Create a Virtual Environment

In this section, you’ll build your project structure. You can name the root folder of your project any way you like. For example, you could name it rp_flask_api/. Create the folder and navigate into it:

In this case, you name the root folder of your project rp_flask_api/. The files and folders that you create over the course of this series will be located in either this folder or its subfolders.

After you navigate to the project folder, it’s a good idea to create and activate a virtual environment. That way, you’re installing any project dependencies not system-wide but only in your project’s virtual environment.

Select your operating system below and use your platform-specific command to set up a virtual environment:

With the commands shown above, you create and activate a virtual environment named venv by using Python’s built-in venv module. The parenthesized (venv) in front of the prompt indicate that you’ve successfully activated the virtual environment.

Add Dependencies

After you’ve created and activated your virtual environment, it’s time to install Flask with pip:

The Flask micro web framework is the the main dependency that your project requires. On top of Flask, install Connexion to handle the HTTP requests:

To also make use of auto-generated API documentation, you install Connexion with the added support for Swagger UI. Later in this tutorial, you’ll learn more about the Python packages that you just installed.

Initiate Your Flask Project

The main file of your Flask project will be app.py. Create app.py in rp_flask_api/ and add the following content:

You import the Flask module, giving the application access to the Flask functionality. You then create a Flask application instance named app. Next, you connect the URL route "/" to the home() function by decorating it with @app.route("/"). This function calls the Flask render_template() function to get the home.html file from the templates directory and return it to the browser.

In short, this code gets a basic web server up and running and makes it respond with a home.html template, which will be served to a browser when navigating to the URL "/".

Flask expects home.html in a template directory named templates/. Create the templates/ directory and add home.html:

Flask comes with the Jinja Templating Engine, which enables you to enhance your templates. But your home.html template is a basic HTML file without any Jinja features. That’s okay for now, because the purpose of home.html is to verify that your Flask project responds as intended.

With the Python virtual environment active, you can run your application with this command line in the directory containing the app.py file:

When you run app.py, a web server will start on port 8000. If you open a browser and navigate to http://localhost:8000, you should see Hello, World! displayed:

Screenshot of Flask Hello World Website.

Congratulations, your web server is running! You’ll extend the home.html file later to work with the REST API that you’re developing.

By now, your Flask project structure should look like this:

rp_flask_api/ │ ├── templates/ │ └── home.html │ └── app.py

This is a great structure for starting any Flask project. You may find that the source code will come in handy when you’re working on future projects. You can download it here:

In the next sections, you’ll expand the project and add your first REST API endpoints.

Adding Your First REST API Endpoint

Now that you’ve got a working web server, you can add your first REST API endpoint. To do this, you’ll use Connexion, which you installed in the previous section.

The Connexion module allows a Python program to use the OpenAPI specification with Swagger. The OpenAPI Specification is an API description format for REST APIs and provides a lot of functionality, including:

When you use OpenAPI with Swagger, you can create a user interface (UI) to explore the API. All of this can happen when you create a configuration file that your Flask application can access.

Create the API Configuration File

The Swagger configuration file is a YAML or JSON file containing your OpenAPI definitions. This file contains all of the information necessary to configure your server to provide input parameter validation, output response data validation, and URL endpoint definition.

Create a file named swagger.yml and begin adding metadata to it:

When you define an API, you must include the version of your OpenAPI definition. You use the openapi keyword for this. The version string is important because some parts of the OpenAPI structure may change over time.

Also, just like each new Python version includes new features, there may be keywords added or deprecated in the OpenAPI specification.

The info keyword begins the scope of the API information block:

Next, add servers and url, which define the root path of your API:

By providing "/api" as the value of url, you’ll be able to access all of your API paths relative to http://localhost:8000/api.

You define your API endpoints in a paths block:

The paths block begins the configuration of the API URL endpoint paths:

Together with the url definition in servers, this creates the GET /api/people URL endpoint that you can access at http://localhost:8000/api/people.

The get block begins the configuration of the single /api/people URL endpoint:

operationId must contain a string. Connexion will use "people.read_all" to find a Python function named read_all() in a people module of your project. You’ll create the corresponding Python code later in this tutorial.

The responses block defines the configuration of the possible status codes. Here, you define a successful response for the status code "200", containing some description text.

You can find the complete content of the swagger.yml file in the collapsible below:

Below, you’ll find the full source code of your OpenAPI definition:

You’ve organized this file in a hierarchical manner. Each indentation level represents a level of ownership, or scope.

For example, paths marks the beginning of where all the API URL endpoints are defined. The /people value indented under that represents the start of where all the /api/people URL endpoints will be defined. The get: scope indented under /people holds the definitions associated with an HTTP GET request to the /api/people URL endpoint. This pattern goes on for the entire configuration.

The swagger.yml file is like a blueprint for your API. With the specifications that you include in swagger.yml, you define what data your web server can expect and how your server should respond to requests. But so far, your Flask project doesn’t know about your swagger.yml file. Read on to use Connexion to connect your OpenAPI specification with your Flask app.

Add Connexion to the App

There are two steps to adding a REST API URL endpoint to your Flask application with Connexion:

  1. Add an API configuration file to your project.
  2. Connect your Flask app with the configuration file.

You already added a configuration file named swagger.yml in the last section. To connect the API configuration file with your Flask app, you must reference swagger.yml in your app.py file:

The import connexion statement adds the module to the program. The next step is creating the application instance using Connexion rather than Flask. Internally, the Flask app is still created, but it now has additional functionality added to it.

Part of the app instance creation includes the parameter specification_dir in line 6. This tells Connexion which directory to look in for its configuration file. In this case, it’s the same directory that you run app.py from.

In line 7, you tell the app instance to read the swagger.yml file from the specification directory and configure the system to provide the Connexion functionality.

Return Data From Your People Endpoint

In the swagger.yml file, you configured Connexion with the operationId value "people.read_all". So, when the API gets an HTTP request for GET /api/people, your Flask app calls a read_all() function within a people module.

To make this work, create a people.py file with a read_all() function:

In line 5, you create a helper function named get_timestamp() that generates a string representation of the current timestamp.

You then define the PEOPLE dictionary data structure in line 8, which is the data you’ll work with in this part of the tutorial series.

The PEOPLE dictionary stands in for a proper database. As PEOPLE is a module variable, its state persists between REST API calls. However, any data that you change will be lost when you restart your web application. This is not ideal, but it’s fine for now.

Then you create the read_all() function in line 26. Your server will run read_all() when it receives an HTTP request to GET /api/people. The return value of read_all() is a list of dictionaries with information about a person.

Running your server code and navigating your browser to http://localhost:8000/api/people will display the list of people on-screen:

Screenshot of REST API JSON response.

Congratulations, you’ve created your first API endpoint! Before continuing on your way to building out your REST API with multiple endpoints, take a moment and explore the API a bit more in the next section.

Explore Your API Documentation

Currently you have a REST API running with a single URL endpoint. Your Flask app knows what to serve based on your API specification in swagger.yml. Additionally, Connexion uses swagger.yml to create API documentation for you.

Navigate to localhost:8000/api/ui to see your API documentation in action:

Screenshot of Swagger UI website

This is the initial Swagger interface. It shows the list of URL endpoints supported at your http://localhost:8000/api endpoint. Connexion builds this automatically when it parses the swagger.yml file.

If you click on the /people endpoint in the interface, then the interface will expand to show more information about your API:

This displays the structure of the expected response, the content-type of that response, and the description text that you entered about the endpoint in the swagger.yml file. Any time the configuration file changes, the Swagger UI changes as well.

You can even try the endpoint out by clicking the Try it out button. This feature can be extremely useful when your API grows. The Swagger UI API documentation gives you a way to explore and experiment with the API without having to write any code to do so.

Using OpenAPI with the Swagger UI offers a nice, clean way to create the API URL endpoints. So far, you’ve only created one endpoint to serve all people. In the next section, you’ll add additional endpoints to create, update, and delete people in your collection.

Building Out the Complete API

So far, your Flask REST API has one endpoint. Now it’s time to build out an API providing full CRUD access to your people structure. As you recall, the definition of your API looks like this:

Action HTTP Verb URL Path Description
Read GET /api/people Read a collection of people.
Create POST /api/people Create a new person.
Read GET /api/people/ Read a particular person.
Update PUT /api/people/ Update an existing person.
Delete DELETE /api/people/ Delete an existing person.

To achieve this, you’ll extend both the swagger.yml and people.py files to fully support the API defined above.

Work With Components

Before you define new API paths in swagger.yml, you’ll add a new block for components. Components are building blocks in your OpenAPI specification that you can reference from other parts of your specification.

Add a components block with schemas for a single person:

To avoid code duplication, you create a components block. For now, you save only the Person data model in the schemas block:

The dash (-) in front of - lname indicates that required can contain a list of properties. Any property that you define as required must also exist in properties, which includes the following:

The type key defines the value associated with its parent key. For Person, all properties are strings. You’ll represent this schema in your Python code as a dictionary later in this tutorial.

Create a New Person

Extend your API endpoints by adding a new block for the post request in the /people block:

The structure for post looks similar to the existing get schema. One difference is that you also send requestBody to the server. After all, you need to tell Flask the information that it needs to create a new person. Another difference is operationId, which you set to people.create.

Inside of content, you define application/json as the data exchange format of your API.

You can serve different media types in your API requests and API responses. Nowadays APIs commonly use JSON as the data exchange format. This is good news for you as a Python developer, because JSON objects look very much like Python dictionaries. For example:

This JSON object resembles the Person component that you were defining earlier in swagger.yml and that you’re referencing with $ref in schema.

You’re also using a 201 HTTP status code, which is a success response that indicates the creation of a new resource.

With people.create, you’re telling your server to look for a create() function in the people module. Open people.py and add create() to the file:

In line 4, you’re importing Flask’s abort() function. Using abort() helps you send an error message in line 20. You raise the error response when the request body doesn’t contain a last name or when a person with this last name already exists.

If the data in the request body is valid, you update PEOPLE in line 13 and respond with the new object and a 201 HTTP code in line 18.

Handle a Person

So far, you’re able to create a new person and get a list with all your people. In this section, you’ll update swagger.yml and people.py to work with a new path that handles a single existing person.

Open swagger.yml and add the code below:

Similar to your /people path, you start with the get operation for the /people/{lname} path. The {lname} substring is a placeholder for the last name, which you have to pass in as a URL parameter. So, for example, the URL path api/people/Ruprecht contains Ruprecht as lname.

You’ll use the lname parameter in other operations, too. So it makes sense to create a component for it and reference it where needed.

operationId points to a read_one() function in people.py, so head over to that file again and create the missing function:

When your Flask app finds the provided last name in PEOPLE, then it returns the data for this particular person. Otherwise, the server will return a 404 HTTP error.

To update an existing person, update swagger.yml with this code:

With this definition of the put operation, your server expects update() in people.py:

The update() function expects the arguments lname and person. When a person with the provided last name exists, then you update the corresponding values in PEOPLE with the person data.

To get rid of a person in your dataset, you need to work with a delete operation:

Add the corresponding delete() function to person.py:

If the person you want to delete exists in your dataset, then you remove the item from PEOPLE.

Both people.py and swagger.yml are complete for this part of the tutorial. You can download the complete files by clicking the link below:

With all the endpoints to manage people in place, it’s time to try out your API. Since you used Connexion to connect your Flask project with Swagger, your API documentation is ready for you when you restart your server.

Explore Your Complete API Documentation

Once you’ve updated the swagger.yml and people.py files to complete the people API functionality, the Swagger UI system will update accordingly and look something like this:

This UI allows you to see all of the documentation that you’ve included in the swagger.yml file and to interact with all of the URL endpoints making up the CRUD functionality of the people interface.

Unfortunately, any changes that you make won’t persist when you restart your Flask application. That’s why you’ll plug a proper database in to your project in the next part of this tutorial series.

Conclusion

In this part of the tutorial series, you created a comprehensive REST API with Python’s Flask web framework. With the Connexion module and some additional configuration work, useful documentation and an interactive system can be put in place. This makes building a REST API a very enjoyable experience.

In the first part of this tutorial series, you learned how to:

In part two of this series, you’ll learn how to use a proper database to store your data permanently instead of relying on in-memory storage as you did here.