How to Create a RESTful API Using Python and Flask
Traducciones al EspañolEstamos traduciendo nuestros guías y tutoriales al Español. Es posible que usted esté viendo una traducción generada automáticamente. Estamos trabajando con traductores profesionales para verificar las traducciones de nuestro sitio web. Este proyecto es un trabajo en curso.
Flask is a Python micro-framework for building web applications and web APIs. The framework provides pared-down core functionality, however, it is highly extensible. This guide shows you how to use Flask to build a REST API that serves up information about different programming languages. The data information exposed by the API can also be referred to as a resource. The API’s data comes from Hillel Wayne’s research on influential programming languages. At the end of the guide, you have an API that allows clients to complete the following:
- GET all programming languages stored in the API
- GET a specific instance of a programming language
- Filter the programming language resources based on the publication year field
- POST, PUT, and DELETE a programming language instance
NoteGET, POST, PUT, and DELETE are HTTP request methods used to perform an action on a resource.
How to Create REST API Endpoints with Flask
The REST protocol gives clients access to resources stored in a database and allows clients to perform operations on the stored data. The operations are known as CRUD operations (create, read, update, and delete). The following sections show you how to create the CRUD operations for your Flask web API.
Install Flask
NoteThis section makes use of the virtualenv tool to create a virtual environment on your system. Follow the installation steps in our How to Create a Python Virtual Environment guide if you do not have virtualenv installed on your computer.
Create a directory to store your Flask web application and move into the directory.
mkdir example_app && cd example_app
Inside the
example_app
directory, create a new file namedprog_lang_app.py
.mkdir example_app && cd example_app touch prog_lang_app.py
Create and activate a virtual environment using the following command:
python3 -m venv venv . venv/bin/activate
In order to run a Flask server, you install Flask first using the Python Package Index (pip). Use the following command to install Flask:
pip install flask
Create the List Endpoint in Flask
RESTful services typically have two endpoints used to retrieve (GET) resources. One endpoint lists all resources or filters them according to some criterion. The second endpoint retrieves the details of a specific resource based on a unique identifier. In this section, you create two endpoints to GET resources from your API. This section may refer to these endpoints as the list
and details
endpoints.
NoteAll the steps in this section edit the same file,prog_lang_app.py
.
In your preferred text editor, open the
prog_lang_app.py
file and add the following lines:- File: prog_lang_app.py
1 2 3
from flask import Flask app = Flask(__name__)
These lines import Flask, and instantiate the app. You can instantiate the class
Flask
and assign it to a variable (traditionally, this variable is namedapp
)Note
Although RESTful APIs typically access data from a database, this tutorial does not cover the details of integrating with a database.Create a small in-memory data store (Python dictionary) to store the data related to programming languages. Place this code underneath the import and app instantiation lines.
1 2 3 4 5 6 7
... in_memory_datastore = { "COBOL" : {"name": "COBOL", "publication_year": 1960, "contribution": "record data"}, "ALGOL" : {"name": "ALGOL", "publication_year": 1958, "contribution": "scoping and nested functions"}, "APL" : {"name": "APL", "publication_year": 1962, "contribution": "array processing"}, }
The code creates the
in_memory_datastore
nested dictionary with entries for three programming language. Each programming language dictionary has keys for its name, the approximate year it was published, and its contributions to modern programming languages.Create the
list
endpoint with the code in the example below. Below thein-memory datastore
dictionary, a rudimentary list endpoint fetches all the programming language resources and displays them as JSON.Note
RESTful APIs are generally organized around a resource. A resource refers to the database records that an API gives clients access to. In the case of this tutorial, the resource is an instance of a programming language.- File: prog_lang_app.py
1 2 3 4 5
... @app.get('/programming_languages') def list_programming_languages(): return {"programming_languages":list(in_memory_datastore.values())}
Requests can be sent to the
/programming_languages
URL using the GET HTTP verb. The request should be sent without any parameters. This endpoint fetches all the records in the datastore. It returns a JSON object with the keyprogramming_languages
. This key points to all the records and is represented as an array.Note
There are two reasons to put the list into an object with a label, rather than returning the raw array.
Maintainability: an object provides the freedom to add more attributes to the return body later. For example, suppose you wanted to return a count of the objects in the database. You cannot add a count attribute to a raw array, but you can add a count attribute to an enclosing JSON object with one key that points to an array. This is especially useful in APIs that allow clients to filter sections of data or to request aggregate metrics for the data or sections of data.
Security: ten years ago, browsers didn’t guard against malicious actors redefining the JSON array and obtaining access to raw JSON array payloads. It’s all patched now, but the programming community keeps on wrapping arrays in objects because of the maintainability benefit.
Start the app to view the data returned by the
list
programming languages endpoint. Navigate to the directory where you stored the app. Then, run the following commands:export FLASK_APP=prog_lang_app.py flask run
Open a browser and visit
http://127.0.0.1:5000/
to access the app running locally on your computer.Visit
http://127.0.0.1:5000/programming_languages
in the browser to view the JSON object containing the contents of the datastore you created.
Create the Detail Endpoint in Flask
The next step is to add an endpoint to retrieve a specific programming language resource from the datastore. The details
endpoint has an interpolated variable in the endpoint string called programming_language_id
. This variable allows you to query for a specific item in your datastore. The id
refers to the index in the list related to a specific instance of a programming language resource. This presents a problem once clients can delete items from the datastore. This is fixed in a later section.
Update the
prog_lang_app.py
file to add the code that creates your app’s detail endpoint. This code goes underneath the code for listing the programming languages:- File: prog_lang_app.py
1 2 3 4 5
... @app.route('/programming_languages/<programming_language_name>') def get_programming_language(programming_language_name): return in_memory_datastore[programming_language_name]
Run the app and visit
http://127.0.0.1:5000/programming_languages/COBOL
in your browser. You should see a similar output returned by your API.{"contribution":"record data","name":"COBOL","publication_year":1960}
Add Filters to the List Endpoint
Update the prog_lang_app.py
file’s in_memory_datastore
dictionary with a few more programming language entries. Increasing the size of the datastore provides you with more data to filter. In this section, you add code to filter the list
endpoint using a specific date range.
Open the
prog_lang_app.py
file and edit thein_memory_datastore
to add the additional entries.- File: prog_lang_app.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
... in_memory_datastore = { "COBOL": {"name": "COBOL", "publication_year": 1960, "contribution": "record data"}, "ALGOL": {"name": "ALGOL", "publication_year": 1958, "contribution": "scoping and nested functions"}, "APL": {"name": "APL", "publication_year": 1962, "contribution": "array processing"}, "BASIC": {"name": "BASIC", "publication_year": 1964, "contribution": "runtime interpretation, office tooling"}, "PL": {"name": "PL", "publication_year": 1966, "contribution": "constants, function overloading, pointers"}, "SIMULA67": {"name": "SIMULA67", "publication_year": 1967, "contribution": "class/object split, subclassing, protected attributes"}, "Pascal": {"name": "Pascal", "publication_year": 1970, "contribution": "modern unary, binary, and assignment operator syntax expectations"}, "CLU": {"name": "CLU", "publication_year": 1975, "contribution": "iterators, abstract data types, generics, checked exceptions"}, } ...
Now, you can add the code to allow clients to filter on the
publication_year
parameter. First, add theFlask.request
object to yourprog_lang_app.py
file’s import statement as shown below:- File: prog_lang_app.py
1 2 3 4
from flask import Flask, request app = Flask(__name__) ...
Next, change the
list_programming_languages()
function to act upon the query parametersbefore_year
andafter_year
.- File: prog_lang_app.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
... @app.get('/programming_languages') def list_programming_languages(): before_year = request.args.get('before_year') or '30000' after_year = request.args.get('after_year') or '0' qualifying_data = list( filter( lambda pl: int(before_year) > pl['publication_year'] > int(after_year), in_memory_datastore.values() ) ) return {"programming_languages": qualifying_data}
Clients can now filter the programming languages with two query parameters:
before_year
andafter_year
. Flask automatically treats all parameters passed to a routed function (besides interpolated path parameters) as query parameters. If a client does not pass any query parameters, the default start year of0
and the default end year of30,000
automatically capture all languages.The resulting objects go through a filter that picks out only the ones with a
publication_year
between thebefore_year
and theafter_year
. A request without those query parameters continues to deliver all of the programming languages.
Build a Create Endpoint
So far, all the endpoints expect clients to use the GET HTTP verb to make their requests. In this section, you write the code to support the POST HTTP verb. The create
endpoint expects a POST verb as well as a request body. The request body is a payload of data that specifies the attributes of the new resource that the client wants to add. In this case, those attributes are sent as a JSON object. These attributes include the name
, publication_year
, and contribution
of the programming language being added.
To use the same route in your API with different request verbs, write your code under the same annotation. Then, use conditional logic to route the request to the correct place. To do this, edit your
prog_lang_app.py
file to remove the@app.get
annotation and modify it as shown below:- File: prog_lang_app.py
1 2 3 4 5 6 7 8
... @app.route('/programming_languages', methods=['GET', 'POST']) def programming_languages_route(): if request.method == 'GET': return list_programming_languages() elif request.method == "POST": return create_programming_language(request.get_json(force=True))
Now, add the new
create_programming_language
method belowlist_programming_languages()
method:- File: prog_lang_app.py
1 2 3 4 5 6 7
... def create_programming_language(new_lang): language_name = new_lang['name'] in_memory_datastore[language_name] = new_lang return new_lang
The two helper functions handle listing programming languages, in the case of a GET request. In the case of a POST request, the second helper function creates a new programming language resource. Use cURL to create a programming language on the command line:
curl -X POST http://127.0.0.1:5000/programming_languages -H 'Content-Type: application/json' -d '{"name": "Java", "publication_year": 1995, "contribution": "Object-oriented programming language."}'
Once you have created the new resource, make a GET request to
http://127.0.0.1:5000/programming_languages
.curl http://127.0.0.1:5000/programming_languages
Notice that a resource for
Java
is returned in the JSON object.
Create the Update Endpoint
To update a resource, you send a PUT request with a request body to the URL of the record you want to update. To achieve this you, use a similar tactic to the one you used in the previous section.
Remove the the
@app.route
annotation and theget_programming_language()
function. Replace them with the following code:- File: prog_lang_app.py
1 2 3 4 5 6 7 8
... @app.route('/programming_languages/<programming_language_name>', methods=['GET', 'PUT']) def programming_language_route(programming_language_name): if request.method == 'GET': return get_programming_language(programming_language_name) elif request.method == "PUT": return update_programming_language(programming_language_name, request.get_json(force=True))
Now, add the new
update_programming_language()
function below theget_programming_language()
function:- File: prog_lang_app.py
1 2 3 4 5 6
... def update_programming_language(lang_name, new_lang_attributes): lang_getting_update = in_memory_datastore[lang_name] lang_getting_update.update(new_lang_attributes) return lang_getting_update
To test your new endpoint, send a request to it to update an existing resource. For example, send a request using Postman similar to the following:
curl -X PUT http://127.0.0.1:5000/programming_languages/Java -H 'Content-Type: application/json' -d '{"contribution": "The JVM"}'
Send a GET request to the
list
endpoint to view the update made to the Java contribution you issued in your PUT request.curl http://127.0.0.1:5000/programming_languages
Create the Delete Record Endpoint
The endpoint to delete a record is similar to the update endpoint. The difference is the HTTP verb that you use. RESTful services conventionally use a DELETE verb for the delete endpoint.
Update your
@app.route
annotation to include theDELETE
method, as shown below:- File: prog_lang_app.py
1 2 3 4 5 6 7 8 9 10
... @app.route('/programming_languages/<programming_language_name>', methods=['GET', 'PUT', 'DELETE']) def programming_language_route(programming_language_name): if request.method == 'GET': return get_programming_language(programming_language_name) elif request.method == "PUT": return update_programming_language(programming_language_name, request.get_json(force=True)) elif request.method == "DELETE": return delete_programming_language(programming_language_name)
Notice the addition of
DELETE
is passed to the method’s parameter in the annotation.Next, add the
delete_programming_language()
function below theupdate_programming_language()
function:- File: prog_lang_app.py
1 2 3 4 5 6
... def delete_programming_language(lang_name): deleting_lang = in_memory_datastore[lang_name] del in_memory_datastore[lang_name] return deleting_lang
To delete a resource, use your preferred request client and issue the following request:
curl -X DELETE http://127.0.0.1:5000/programming_languages/COBOL
The return value is the COBOL JSON object. A visit to the
List
endpoint reveals that COBOL is no longer in the list of languages.
You can view the entirety of the app in
the example prog_lang_app.txt
file. This file contains all endpoints created in this guide. Flask includes many specialized options in addition to the basics covered in this guide. Refer to
Flask’s official documentation to learn how to enhance the API created in this tutorial.
This page was originally published on