We have the answers to your questions! - Don't miss our next open house about the data universe!

Programming and documenting an API with Python, Flask, Swagger and Connexion

- Reading Time: 7 minutes
Programming and documenting an API with Python, Flask, Swagger and Connexion

In the previous two articles, we saw a first example of programming a Web API with Flask and how to connect a Web API to an SqLite database.

In what follows, we’ll look at how to build REST APIs with input and output validation and documentation automatically generated by Swagger/OpenAPI. We’ll also see how to use the API with JavaScript to update the DOM (Document Object Model), i.e. the structure of the web page displayed. The REST API we’re going to build will enable us to manipulate a directory of people, identified by their surname. Updates will be marked with a “timestamp” (date/time). The directory could be manipulated in the form of a database, a file, or via a network protocol, but to simplify things initially, we’ll use in-memory data.

One of the aims of APIs is to decouple data from the applications that use it, by making the details of data implementation transparent.

1. REST methodology

We mentioned the REST methodology in the first article. We’ll now take a closer look.

REST can be thought of as a set of conventions leveraging the HTTP protocol to provide CRUD (Create, Read, Update, Delete) tools acting on entities or collections of entities over the internet.

CRUD can be mapped to HTTP in the following way:

2. Ce que REST n’est pas

Because the REST methodology is so useful and helps to fine-tune the way you interact with an API, it is sometimes misused, for problems to which it is not suited.

In many cases, we want to perform an action directly. Let’s take the example of substituting a string.

Here is an example of a URL allowing a priorito do this:

/api/substituteString/<string>/<search_string>/<sub_string>

Here, denotes the string on which we want to make the substitution, is the substring to be replaced and is the new substring. You can most certainly design code on the server that will perform the desired operation, but this poses problems in terms of REST.
  • First problem: the URL doesn’t point to a single resource, so what it returns depends entirely on the path specified.
  • Second problem: No CRUD concept matches this URL.
  • Third problem: The meaning of the path variables depends on their position in the URL.
This could be fixed by modifying the URL to use a query: /api/substituteString? string=<string>&search_string=<search_string>&sub_string=<sub_string> But the URL portion /api/substituteString does not designate a entity (is not a name) or a collection of entities: it designates an action (it is a verb). This does not fall within the REST conventions and therefore does not correspond to an API. What the previous URL represents is actually a RPC (Remote Procedure Call). Conceptualising what you want to do as an RPC is more relevant. This is all the more so because REST and RPC conventions can coexist without any problems within an API.

In the following example, we’re going to develop a REST API to access a directory of people and perform CRUD operations on it.

Here’s the API design:

We’re going to start by creating a very simple web server using the Flask development framework.We create a directory api, a subdirectory version_1, then a subdirectory templatesor save the following programs: server.py.

templates/home.html

We’re going to start by creating a very simple web server using the Flask development framework.

We create a directory api, a subdirectory version_1, then a subdirectory templatesor save the following programs:

server.py.We’ll start by creating a very simple web server using the Flask development framework.

We create a directory api, a subdirectory version_1, then a sub-catalogue templatesor save the following program:

server.py.

4. Connexion

Now that we have a working Web service, we can add a REST API endpoint. To do this, we’ll install the Connexion module.

To do this, type the following in a terminal window (assuming you are working under Anaconda):

conda install -c conda-forge connection

You can now import it into Python.

Next, create a version_2 subdirectory in the api directory and save the following programs there:

server.py

swagger.yml

In order to use Connexion, we first import it, then create the application using Connexion rather than Flask. The Flask application is still created underneath, but with additional functionality. The creation of the application instance includes a specification_dir parameter. It tells Connexion where to find the configuration file, in this case the current directory. The line app.add_api (‘swagger.yml’) tells the instance to read the file swagger. yml in the specification_dir and configure the system for Connexion. The file swagger. ymlfile is a YAML (or JSON) file containing the information needed to configure the server, ensure input and output validation, provide request URLs and configure the Swagger user interface (UI). The preceding code defines the GET / api/people request. The code is organized hierarchically, with indentation defining the degree of membership or scope. For example, paths defines the root of all API URLs. The /people indented following defines the root of all /api/people URLs. The get: indented following defines a GET request to the URL /api/people, and so on for the entire configuration file. In the file swagger. yml, we configure Connection with the value operationId to call the module people and the read function of this module when the API receives an HTTP request of type GET /api/people. This means that a people.py module must exist and contain a read( ) function. Here is the people.py module: people.py

In the module people. py first appears the get_timestamp( ) function, which generates a representation of the current date/time in the form of a string. It is used to modify data when a change request is passed to the API.

Next, we define a PEOPLE dictionary which is a simple database of names, having the surname as the key.

PEOPLE is a module variable and its state therefore persists between API calls.

In a real application, PEOPLE data would reside in a database, file or web resource, i.e. it would persist regardless of application execution.

Then comes the read( ) function, which is called when the server receives an HTTP GET request for /api/people.

The return value of this function is converted to JSON format (as specified by produces: in the configuration file swagger. yml).

This consists of the list of people sorted alphabetically by surname. At this point, we can run the program server.py using the command:

$ python server.py

which gives the following result by entering as address http://0.0.0.0:8080/api/people in the browser bar:

5. Swagger/OpenAPI

We’ve thus built a simple API that allows a single request. However, we can ask ourselves the following question, especially since we saw how to obtain the same result much more simply with Flask in our first article: What does configuration using the swagger.yml file provide? In fact, in addition to the API a Swagger User Interface (UI) (OpenAPI) has been created by Connexion. To access it, simply type http://localhost:8080/api/ui. We get the following result:

Click on People to display the potential query:

When you click on the request, the interface displays the following additional information:

The structure of the response
The format of the response (content-type)

The text entered in swagger.yml about the request

You can even try out the query by clicking on the Try It Out! at the bottom of the screen. The interface expands the display even further and the result is:
This is all very useful when you have a complete API since it allows you to test and experiment with the API without writing any code. As well as being handy for accessing documentation, the Swagger/OpenAPI interface has the advantage of being updated as soon as the swagger.yml file is updated. In addition, the swagger.yml provides a methodical way of designing all the queries, ensuring a rigorous API design. Finally, it decouples the Python code from the query configuration, which can be very useful when designing APIs that support Web applications. We’ll see an example of this in the sequel.

6. Full API

Our goal was to create an API offering CRUD access to our PEOPLE data. To do this, we are completing the swagger.yml configuration file and the people.py module to meet our API specifications. The completed files are accessible in the subdirectoryversion_3 under GitHub: https://github.com/salimlardjane1/projects/tree/version_3. Executing server.py then gives, as far as the Swagger/OpenAPI interface is concerned:.
The previous interface provides access to the documentation populated in the swagger.yml file and interacts with all the URLs implementing the API’s CRUD functionality..

7. Web application

We now have a documented REST API that we can interact with using Swagger/OpenAPI.. How do we go further? The next step is to create a Web application illustrating the practical use of the API. We create a web application that displays the people in the database on the screen and allows us to add people, update people’s data and delete people from the database. This will be done using AJAX requests issued from JavaScript to the URLs in our API. To begin with, we need to complete the home.html file, which controls the structure and appearance of the web application’s home page, as follows: The home.html file is available on GitHub: In the templates subdirectory of the version_4. directory. The HTML program completes the initial home.html program by adding a call to the external normalize. min.css (https:// necolas.github.io/normalize.css/), which is used to normalise the display on different browsers. It also makes use of the jquery-3.3.1.min.js file (https:// jquery.com), which provides jQuery functionality in JavaScript. These are used to program the interactivity of the page. The HTML code defines the static part of the application. The dynamic parts will be provided by JavaScript when the page is accessed and when the user interacts with it. The home.html file refers to two static files: static/css/home. css and static/js/home.js. The directory named static is immediately recognised by the Flask application and the contents of the css and js is thus accessible from the home. html. The files home.css and home. js are available on GitHub: We’ll talk more about CSS and JavaScript in the next article. When we go to the version_4 directory and run server. py, we get the following result in the browser:
The Create button allows the user to add a person to the catalogue on the server. Double-clicking on a line in the table brings up the first and last name in the input fields. To update, the user must change the first name, the surname being the search key. To delete a person from the directory, simply click on Delete.. Reset empties input fields. So we’ve built a small, functional web application. We’ll look at how it works in more detail in the next article.

8. Références bibliographiques

    • Flask Web Development : Developing Web Applications with Python (2nd edition). M. Grinberg. O’Reilly 2018.
    • Architectural Styles and the Design of Network-Based Software Architectures. T. Fielding. Thesis, University of California, 2000.

You are not available?

Leave us your e-mail, so that we can send you your new articles when they are published!
icon newsletter

DataNews

Get monthly insider insights from experts directly in your mailbox