Python Backend with Flask for Beginners
This article provides the reader some insights on the world of Python backend development, without claiming to be exhaustive: we would like to introduce the most important and commonly used components.
This article provides the reader some insights on the world of Python backend development, without claiming to be exhaustive: we would like to introduce the most important and commonly used components. We - who come from the world of Java Spring Boot - would like to shed light on the fact that Spring is not the only available option, although it would be worth writing a separate article on why to choose Spring Boot instead of Python Flask or vice versa…
Without further stalling, let’s dive into this fascinating topic!
How to Rest Controller
What exactly is REST and how can we describe its functioning?
REST, or REpresentational State Transfer, is actually a type of architecture that includes web rules that allow systems that comply with REST conventions to easily communicate with each other over the Internet. REST technology communicates via the HTTP standards. Thanks to the predefined methods of HTTP, it is possible to quickly and comprehensively develop services for CRUD (Create, Read, Update, and Delete) operations, which manage resources or collections of resources.
The basic idea of python is to implement simple, generic systems like a REST controller (which processes incoming REST requests) efficiently and quickly. This will be the case here as well.
You can create an HTTP controller with the Flask package of python. After creating a new python file (app.py) and downloading the required flask dependency (pip install flask), by writing a few lines of code, you can immediately start a web service running on localhost with the following command: python app.py.
If you want the given API endpoint to produce an HTML output, E.g a proper user interface or a form, you can attach template files written in HTML to pose as the return value of the given url path:
In order to do so, you need to import the render_template dependency, so then you can return the user-created page depending on the API endpoint by specifying the HTML template source folder.
The HTML rendering section will not be discussed further, as if the application also has a user interface, it is better to use a more mature web framework, such as Angular, React, or Vue.js.
Once the REST controller is running, due to the intuitive use of the language and the flask package, you can quickly add and manage new endpoints by typing a few lines of code. By retrieving a received request, you can check, among other things, what HTTP method was called, what the parameters of the request were, or what is in the body of the request.
You can use the request get_json function to convert the body payload to json format. Naturally, to do this, the sending party must set the request content type to application/json.
If the sole purpose of the REST controller is to manage the resources behind it using CRUD operations, then there is an even better and more transparent solution. This is the Flask-RESTful package.
The Flask-RESTful dependency will make the code even more transparent, as the representation of the endpoint resource will be a python class within which we can define the operations required for HTTP methods. If we create such a class, it must then be registered in the API, so Flask will be able to integrate it into the REST controller.
How to ORM/Repo
The API layer for the Python application was created in the previous chapter, but that's not all. If we need persistent data storage while using the service, we need to save our data to a database in some way, as the REST architecture is not stateful by design, and the submitted HTTP methods do not preserve any historical data from the user's previous requests.
Naturally, due to the versatile nature of Python, there is a Python wrapper class for every known relational database that can be integrated to access the functionality of that database device. This methodology requires downloading the Python package for that database first, and then we need to open a connection to the desired database. Then, with the language of that relational database, such as SQL, we can influence the stored data.
In our case, however, we don't want to bother with SQL queries. Let's think how much easier it would be if we could immediately bind our objects to database tables (this is ORM = Object Relational Mapping) and replace complex SQL queries with built-in function calls. Fortunately, there is a solution and this is the SQLAlchemy package!
After downloading and importing the dependency, first we must connect to the desired database. The connection might be string database dependent, therefore we should follow the conventions of the given technology!
Next we need to declare a Base class from which we derive the objects we want to map to the database:
We will then be able to create classes that will represent the tables in that database. To do this, we must derive from the Base class defined above, and then we must set the desired table name and primary key:
Each class created in this way is part of the collection of a larger entity, the MetaData class. By retrieving this, we can influence the structure of the entire database. We can use the create_all method to create related tables in the database.
To access the database, and to query or modify its tables, we must open a session. This session object will have auxiliary functions that will allow us to quickly access and perform basic database operations. After completing the operations, the session changes must be committed so that the changes could be written in the database. Below is an insertion of a line:
We can also use the session to delete and add multiple rows to the database at once. If we want to modify or query data, we will need a query builder, which is fortunately provided by SQLAlchemy. For example, you can retrieve more data as follows:
With the query builder we have the opportunity to write more complex queries and data updates. In the following example, only the rows that meet the criteria will be updated:
Naturally, we can add filters to any query. Notice how simple it is, as we don’t have to write SQL queries!
From now on, we can use the usual SQL operators, in the form of a function call, by calling the querybuilder. E.g. LIKE, IN, AND, OR, FIRST. So there is no need to examine the syntax errors of SQL queries, we can safely focus on the functionality of the program rather than the problems of embedding the database query language.
How to Service
Anyone who has some basic understanding in the Spring backend framework knows well what a Service means when we look at the architecture of the application. A service is a separated, independent, and injectable component that represents some kind of business logic implementation. But how does this look specifically in Python? The answer is simple… it doesn’t exist!
The reason for that is that Python is not capable of this level of automation, as this is not the purpose of it. By using Spring Boot, the framework handles automatic configuration and dependency management, while Python does not provide it at all, or only partially, as the essence of Flask and Python is that we can develop CRUD functionalities as easily and quickly as possible.
However, regardless of the fact that there is no automatic dependency management and configuration and no @Service annotation, we can still implement this layer in Python, we only have to configure and manage them for ourselves.
A simple example for Service:
Authentication / Authorisation, Annotations
We already have controllers, we can develop a Rest API, access the database, manage the business logic separately from the other layers of the application, but there is something very important to be developed yet that should be part of every backend: the appropriate protection of our Rest API from unauthorized users.
In the following section, we will show you a very simple example of how we can implement authorization management in python.
The first step is to create a class that will be responsible for authorization management:
In the example above, the login method is given a username and a password, and if they do not match the defined values, it simply returns a 401, 'Unauthorized' response.
If the logic - which we otherwise sophisticate the way we want to: we can use jwt and/or retrieve users from a database - works, for the sake of simplicity, we generate a token that contains only the username and password, and then return that token in response.
The second step is to implement the other side, i.e. we check whether the obtained token is correct:
The code above first checks if the received token is correct in its format, if not then classifies it as invalid. After that, it decrypts the token and then checks for correctness as before.
Cool, we're done with login and token validation! But how do we use the things implemented above live on a controller? The answer takes us through a new class to a solution.
Let's create a new global method, optionally in a new file:
In the example above, we create a so-called wrapper method, which can be used later as an annotation. Simply put, we define a method that will always be the method where we want to use the annotation in the future (the one which we want to protect), and then we create another one in the defined method that will run every time before the “external” method is called.
In this case, for each controller we want to protect, we'll check to see if there is an authorization header before it runs, and if so, check it with our previously written authenticator to make sure the token is correct.
Finally, we use our interceptor on any method by using the @auth_interceptor annotation as follows:
Based on the above, we can state - although this is not the first thing a software developer thinks about when it comes to backend design - that the Python language has all the features and ancillary features similar to the competitor solutions, which are essential for building modern server-side architectures. Furthermore, Python is one of the most sought after and most actively used programming languages, so we don’t have to worry about running into a problem which has no solution. Due to its expressiveness it is also easier to learn and read than Java. Finally, if the backend would lead us to neural networks and artificial intelligence during development, we couldn't find a better language than Python, as many of its free libraries are reserved exclusively for this purpose.
We hope that you found this blog post useful! Do not hesitate to follow us, so you won’t miss interesting stories in the future either. This article was provided by Dávid Horváth, CTO and Marcell Réti, Lead Architect at ff.next. Melinda Havas, Head of Marketing & Business Development and Bence Siklós, Business Analyst formed the content into this article.
Introduction to ChatGPT from a Developer's perspective
The ChatGPT API, developed by OpenAI, has impressive language processing capabilities based on tokens, making AI more accessible and useful for a wide range of organisations and individuals. In this article, we explore some of the key areas of OpenAI's research and how they are advancing the field of AI.
Libraries for React - ff.next’s highly regarded packages
In this article, we present our selection of packages that play a crucial role in the three fundamental aspects of React frontend development: state management, form management, and building a design system.