Flask - getfutureproof/fp_guides_wiki GitHub Wiki
If you would like a lightweight backend framework like Express but would prefer to work in Python rather than Node, you have a great option in Flask. It is an extremely popular option for web application backends as you can get going with very little but it can be extended as far as you need. In this walkthrough we will setup a simple API. For ways to extend your application to include eg. SSR, authentication etc. see the resources links below and most importantly, search for them! A good place to start is this flask extensions article.
-
pip install flask
(replace pip with your installer of choice if appropriate)
Lets make a file for our server to live eg. server.py
-
Import flask with
from flask import Flask
-
Create the server with
server = Flask(__name__)
-
Write the command that will run the server
server.run()
(add argument ofdebug=True
for auto restart on file changes) -
In some cases, the default server that flask offers is not powerful enough or doesn't meet the requirements you need. In that case you will want to use WSGI
-
WSGI is a simple calling convention for web servers to forward requests to web applications or frameworks written in the Python programming language
-
Some of the advantages of WSGI are: more flexibility, scalability, speed and a reusable middleware.
-
In a Linux or MAC machine we recommend using 'gunicorn'. In a Windows machine 'waitress'. Feel free to explore other options!
-
To be able to use wsgi you will want to install the wsgi server of your choice you will only need to create the 'wsgi.py' file, import app and get the app to run.
Now you have a file, run it from your terminal with python server.py
or with the script that you have decided for your wsgi.py file. You can use either.
- Visit the given port (usually 5000 by default with flask)
- Note that 'the requested URL was not found on the server'
- So the server is running, but it does not have any routes
To add a basic, GET route, start with the route
decorator called on your server (this will be the variable you set to Flask(__name)
). Pass the route decorator a string of the path you are writing a rule for.
Directly underneath this you can write the method that will run when this endpoint is requested.
@server.route('/') # @<var-you-set-to-Flask(__name__)>
def home(): # this will run when a GET request to '/' is made
return 'Hello from Flask!' # this plain-text will be sent in the response
For basic CORS configuration, use the flask_cors library and wrap your server before running it. This will enable cross origin access on all routes, from any origin. To get more specific, check the documentation!
pip install flask-cors
from flask_cors import CORS
CORS(server)
To send a JSON response, you just need to import jsonify
from the flask
library and pass it your data.
from flask import jsonify
return jsonify(data)
all_cats = [
{'id': 1, 'name': 'Ziggy'},
{'id': 2, 'name': 'Zelda'}
]
@server.route('/api/cats')
def cats():
return jsonify({'cats': all_cats})
To extract a JSON body from a request, you can use request.get_json()
. Another option, if you want to make sure the headers of the incoming request declare a content-type of application/json, is request.json
. Import request
from flask and you're good to go.
from flask import request # ...etc
# in eg. a POST route
new_cat_data = request.get_json()
Of course, we're likely to want to handle more than just GET requests. We can pass an additional argument to the route
decorator where we state which HTTP verbs this rule should handle.
@server.route('/cats', methods=['POST'])
def create_cat(): # this will run only when a POST request to '/cats' is made
return 'Have some cats' # this plain-text will be sent in the response
We are likely to expect /cats
to respond to both GET and POST requests. You can add as many verbs as you wish to the methods list and in the method, check the request.method to decide what to do next!
from flask import request # ...etc
@server.route('/api/cats', methods=['GET', 'POST'])
def cats():
if request.method == 'GET':
return jsonify({'cats': all_cats})
if request.method == 'POST':
new_cat = request.get_json()
all_cats.append(new_cat)
new_cat['id'] = len(all_cats)
return jsonify({'cat': new_cat})
The if statement approach is common in this setup but you could also consider a dictionary lookup:
def all(r):
return {'cats': all_cats}
def create(r):
new_cat = r.get_json()
all_cats.append(new_cat)
new_cat['id'] = len(all_cats)
return new_cat
@server.route('/api/cats', methods=['GET', 'POST'])
def cats():
fns = {
'GET': all,
'POST': create
}
resp = fns[request.method](request)
return jsonify(resp)
You can declare route parameters in your path inside <>
with optional typing (eg. int:
, string:
). They are passed as named arguments to the handler function.
@server.route('/api/cats/<int:cat_id>')
def cat(cat_id):
cat = all_cats[cat_id-1]
return jsonify(cat)
Query parameters are available in a dictionary via request.args
:
In a request to:
http://localhost:8000/cats/name_generator?first_name=Beth&birth_month=January&birth_date=8
request.args
would point to:
{'first_name': 'Beth', 'birth_month'='January', 'birth_date': '8'}
In this example we have only been returning JSON data but you can, of course return almost anything, including HTML. There are certain benefits of server side rendering, especially around SEO.
To dynamically create HTML content, Flask gives us instant access to the Jinja templating engine, although there are plenty of other options. You can even use do SSR React, as long as you re-hydrate
your React app on the client side!
You can take this a lot further but this is a basic templating example:
# in server file
from flask import request, render_template # ...etc
from dragon import name_generator
@server.route('/name_generator')
def get_name():
dragon_name = name_generator(request.args)
return render_template('result.html', title=f'Hello {dragon_name}!', result=dragon_name)
<!-- in templates/result.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{title}}</title>
</head>
<body>
<h2>Your dragon name is...</h2>
<h1>{{result}}</h1>
</body>
</html>
There are many options to extend the functionality of your Flask app. Search PyPi to discover more! Here are just a few:
-
Database connections
- Flask PyMongo for MongoDB databases
- Flask SQLAlchemy for SQL databases
-
SMTP (sending emails)
-
Authentication
Official Flask Documentation
Mega Tutorial
PyPi
Jinja Templating
React SSR with Flask
Testing with pytest-flask