First Flask App¶
Writing The App¶
The microservice we create may be used for many clients, with many endpoints. At some point, we may want to control the end points a client has access based on the client's group. As well, due to various reasons, we may want to to isolate the resources like data base for different users. One way of achieving this is to implement authentication and maintain the user's previlage to access resources. To give the protection at higher level, we can run multiple applications that has isolated environments, custom configurations and security and data separation. For some use cases we may implement different endpoints. In this scenario, we use Application Factories
.
In this patern, the idea is to set up the application in a function and create instances by passing configurations. The current app configuration can be accessed from any of the file by importing current_app
from flask
.
Create three files, src/app_factory.py, app1/wsgi1.py and app1/config1.py. The folder structure should be as below and add the code given in this section.
.
├── src
│ ├── app_factory.py
│ ├── config.py
│ └── wsgi.py
├── LICENSE
├── README.md
└── requirements.txt
config.py
¶
# config.py
import os
class ConfigClass(object):
APP_NAME = "Image Repo"
try:
SECRET_KEY = os.environ['FLASK_SECRET_KEY1']
except:
SECRET_KEY = 'Secret!'
PROPAGATE_EXCEPTIONS = True
The SECRET_KEY
configuration is an essential requirement in Flask. It serves as the secret key for cryptographic operations, playing a crucial role in securely signing session cookies, generating secure tokens, and protecting against specific security vulnerabilities.
- In this tutorial, we retrieve the secret key from an environment variable called
FLASK_SECRET_KEY1
. If this environment variable is not found, we fallback to using a default string. When deploying in a production environment, ensure thatFLASK_SECRET_KEY1
is defined and exported as an environment variable. Additionally, remember not to store this key in any repositories and keep it confidential. - Depending on your requirements, you might need to configure different keys for each Flask application you develop.
The PROPAGATE_EXCEPTIONS
configuration option in Flask controls how unhandled exceptions are handled by Flask's error handling mechanism. By default, PROPAGATE_EXCEPTIONS
is set to False
, which means Flask catches unhandled exceptions and handles them internally. * In our specific deployment scenario with uWSGI, setting PROPAGATE_EXCEPTIONS
to True
allows uWSGI to handle exceptions and potentially log them. The details of uWSGI logging will be explained in a later chapter, providing further insights into how exceptions are handled within the uWSGI environment.[TODO:].
app_factory.py
¶
# app_factory.py
from flask import Flask
def create_app(config_object):
app = Flask(config_object.APP_NAME)
app.config.from_object(config_object)
return app
config_object
provided and returns it. wsgi.py
¶
# wsgi.py
from app_factory import create_app
from config import ConfigClass
app = create_app(ConfigClass)
if __name__ == '__main__':
app.run(debug=True)
app
by calling the app factory with ConfigClass
and calls app.run()
in debug mode. The method app.run()
takes few other arguments, other than debug, which we shall discuss in later chapter. I'll also explain why this file is named as wsgi.py
, in later stage Running The App¶
Check the port¶
Flask by default using port 5000. Open a browser window and type http://127.0.0.1:5000 or http://localhost:5000.
If port 5000 is unused, this will show a error page as we have not started the app yet. If you see, some other valid page, you are already running a flask app OR port 5000 is being used by someother application. based on the situation, either kill other process that uses the port or change the port for our application, by modifying app.run
arguments.
app.run(debug=True)
app.run(debug=True, port=5002)
Use any port that is not used in the system. If the port is free, you should get an error page similar to the following.
In firefox, I got
Unable to connect
Firefox can’t establish a connection to the server at localhost:5000.
The site could be temporarily unavailable or too busy. Try again in a few moments.
If you are unable to load any pages, check your computer’s network connection.
If your computer or network is protected by a firewall or proxy, make sure that Firefox is permitted to access the web.
[Try Again]
Run App¶
In the terminal, run the following.
(.venv) image_repo $ python src/wsgi.py
* Serving Flask app 'Image Repo'
* Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5000
Press CTRL+C to quit
* Restarting with stat
* Debugger is active!
* Debugger PIN: 679-157-157
Adding End point¶
Let us implement our first end point /
using blueprint and flask_restful. This page could be rendered from a template as a html also. However, we render JSON created by restful resource.
Lets create a endpoint module landing
with four files in it as below.
└── src
├── endpoint
│ └── landing
│ ├── blueprint.py
│ ├── __init__.py
│ ├── models.py
│ └── resources.py
__init__.py
¶
Leave the __init__.py
file empty. This is required as we consider this as a module while importing files.
models.py
¶
_info = """
This is an API service that provides Microservices using REST API. Please ensure \
that you refer to the appropriate endpoint based on your specific requirements. \
Make sure to consult the API documentation or relevant resources to identify the \
suitable endpoints for your desired functionalities.\
""".strip()
class LandingPageModel:
def __init__(self, name):
self.name = name
self.info = _info
def jsonify(self):
{"hello": self.name, "info":self.info}
This file contains the logics to generate content, will have access to data base etc. This file may be used independent of our Flask/REST framework. You should never import resources.py
or blueprint.py
into this. These files may include external modules and implement various logics to generate content. If more than one model is required, we can implement them and import into this models.py. All the classed in this file must have Model suffix.
resources.py
¶
from flask_restful import Resource, request
from .models import LandingPageModel
class LandingPage(Resource):
def get(self):
name = request.args.get("name")
if not name:
name = "guest"
return LandingPageModel(name), 201
Resource
and preferrably must be thin and call methods from models.py. all the resources related to the given endpoint (group) must be in single resources.py
. The class names should be meaningful and related to the endpoint, Resolving the arguments passed as part of the URL or the body must be done here. blueprint.py
¶
from flask import Blueprint
from flask_restful import Api
from .resources import LandingPage
landing_bp = Blueprint('landing_bp', __name__, url_prefix='')
_api = Api(landing_bp)
_api.add_resource(LandingPage, '/')
Add Landing Page into App¶
Now that we have a landing page ready, let us import it into the app.
# app_factory.py
from flask import Flask
from endpoint.landing.blueprint import landing_bp # Import Landing Page
def create_app(config_object):
app = Flask(config_object.APP_NAME)
app.config.from_object(config_object)
## Landing Page
app.register_blueprint(landing_bp) # Register Landing Page
return app
When the server is running, saving this file will automatically update it, including the new endpoint that was created. If the server is not running, you will need to restart it and then navigate to http://127.0.0.1:5000. Since we are rendering JSON, the browser may display the raw JSON or a formatted JSON content, depending on its settings.
API testing¶
In order to overcome limitations of browsers in supporting all features of HTTP and properly handling the JSON output generated by the server, we rely on API testing tools. These tools are specifically developed to interact with APIs, allowing us to send requests and validate the responses received. Popular options like Postman and Swagger provide browser-based interfaces for conducting API testing. Additionally, there is a command-line tool called httpie
that offers convenient API testing capabilities directly in the CLI/Terminal.
While browser-based tools are generally convenient, having a command-line tool that can be integrated into scripts greatly enhances the efficiency of writing tests. With a command-line API testing tool, it becomes easier to automate testing processes and incorporate them into various workflows.
we can install httpie
using sudo apt install httpie
. httpie
tool gives a commandline executable called http
. refer https://httpie.io/
for more details. devhints.io has a httpie cheetsheet here
Most of the things explained with http
can be easily done with Postman or other API testing tools too..
Let us try it on our server.
(.venv) image_repo $ http http://127.0.0.1:5000
HTTP/1.1 201 CREATED
Connection: close
Content-Length: 335
Content-Type: application/json
Date: Wed, 21 Jun 2023 12:29:55 GMT
Server: Werkzeug/2.3.6 Python/3.11.3
{
"hello": "guest",
"info": "This is an API service that provides Microservices using REST API. Please ensure that you refer to the appropriate endpoint based on your specific requirements. Make sure to consult the API documentation or relevant resources to identify the suitable endpoints for your desired functionalities."
}
(.venv) image_repo $
If we need only body,
(.venv) image_repo $ http -b http://127.0.0.1:5000
{
"hello": "guest",
"info": "This is an API service that provides Microservices using REST API. Please ensure that you refer to the appropriate endpoint based on your specific requirements. Make sure to consult the API documentation or relevant resources to identify the suitable endpoints for your desired functionalities."
}
(.venv) image_repo $
Wind up¶
Summary¶
In this section, we learned how to create a Flask app, how to write resources using flask_restful, and connect it with endpoints using blueprint. we also learned about API testing tool, httpie
.
Git update¶
Now that our application is ready, let us submit to git.
(.venv) image_repo $ git add src/.
(.venv) image_repo $ git commit -m "my firstapp"
[dev cbc752b] my firstapp
7 files changed, 65 insertions(+)
create mode 100644 src/app_factory.py
create mode 100644 src/config.py
create mode 100644 src/endpoint/landing/__init__.py
create mode 100644 src/endpoint/landing/blueprint.py
create mode 100644 src/endpoint/landing/models.py
create mode 100644 src/endpoint/landing/resources.py
create mode 100644 src/wsgi.py
—