This content originally appeared on DEV Community and was authored by Krishnanshu Rathore
In this tutorial, we will learn how to create a simple RESTful API using Flask, a lightweight web framework for Python.
We will also use SQLAlchemy, an ORM (Object-Relational Mapper) that allows us to interact with a database using Python objects. We will use SQLite as our database, but you can use any other database that SQLAlchemy supports.
What is a RESTful API?
A RESTful API (Representational State Transfer) is a way of designing web services that follow some principles:
Each resource (such as a user, a product, a post, etc.) is identified by a unique URI (Uniform Resource Identifier), such as
/users/1
or/products/42
.The client can perform different operations on the resources using HTTP methods, such as GET, POST, PUT, PATCH, DELETE, etc. For example, to create a new user, the client can send a POST request to
/users
with the user data in the request body.To update an existing user, the client can send a PUT or PATCH request to/users/1
with the updated data in the request body. To delete a user, the client can send a DELETE request to/users/1
.The server responds with the appropriate status code and data in the response body, usually in JSON (JavaScript Object Notation) format. For example, if the user creation was successful, the server can respond with a 201 (Created) status code and the created user data in the response body.
If the user update was successful, the server can respond with a 200 (OK) status code and the updated user data in the response body. If the user deletion was successful, the server can respond with a 204 (No Content) status code and no response body.
What is Flask?
Flask is a micro web framework for Python that allows us to create web applications quickly and easily.
It has minimal dependencies and provides us with the essential tools to build web services, such as routing, request and response handling, templating, etc.
Flask is also extensible and supports various extensions that add more functionality to our applications, such as SQLAlchemy for database integration, Flask-RESTful for building RESTful APIs, Flask-JWT for authentication and authorization, etc.
What is SQLAlchemy?
SQLAlchemy is an ORM that allows us to work with databases using Python objects. It abstracts away the low-level details of SQL queries and provides us with a high-level interface to manipulate data.
SQLAlchemy supports various databases, such as SQLite, PostgreSQL, MySQL, Oracle, etc. SQLAlchemy also provides us with declarative models that define our database schema using Python classes and attributes.
We can then use these models to perform CRUD (Create, Read, Update, Delete) operations on our data.
Setting up our project:
To start our project, we need to install some dependencies:
Python 3: You can download it from https://www.python.org/downloads/ or use your preferred package manager.
Pip: A tool for installing Python packages. It should come with Python 3 by default.
Virtualenv: A tool for creating isolated Python environments. You can install it using
pip install virtualenv
.Flask: Our web framework. You can install it using
pip install flask
.SQLAlchemy: Our ORM. You can install it using
pip install sqlalchemy
.Flask-SQLAlchemy: An extension that integrates SQLAlchemy with Flask. You can install it using
pip install flask-sqlalchemy
.
Next, we need to create our project directory and files:
Create a directory called
flask-api
and navigate to it.Create a virtual environment called
venv
usingvirtualenv venv
.Activate the virtual environment using
source venv/bin/activate
on Linux/Mac orvenv\Scripts\activate
on Windows.Create a file called
app.py
that will contain our main application code.Create a file called
models.py
that will contain our database models.Create a file called
config.py
that will contain our configuration settings.
Configuring our application
In our config.py
file, we need to define some configuration settings for our application:
import os
# Get the absolute path of the current directory
basedir = os.path.abspath(os.path.dirname(__file__))
# Define the SQLALCHEMY_DATABASE_URI variable that tells SQLAlchemy where to find our database
# We will use SQLite for simplicity and store it in a file called app.db in our project directory
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'app.db')
# Define the SQLALCHEMY_TRACK_MODIFICATIONS variable that tells SQLAlchemy whether to track changes to the database
# We will set it to False to avoid unnecessary overhead
SQLALCHEMY_TRACK_MODIFICATIONS = False
Creating our application
In our app.py
file, we need to create our Flask application and initialize it with our configuration settings.
To interact with the database using SQLAlchemy, we need to create a db
object and associate it with our Flask application. This can be achieved by importing the SQLAlchemy
class from the flask_sqlalchemy
module and creating a db
object in a separate file called db.py
.
Here is an example code snippet for creating the db
object:
from flask_sqlalchemy import SQLAlchemy
# Create a SQLAlchemy object
db = SQLAlchemy()
Then, in the main application file app.py
, we can create a Flask application and associate it with the db
object by calling the init_app()
method of the db
object. Here is an example code snippet:
from flask import Flask
from db import db
# Create a Flask application with the name of the current module
app = Flask(__name__)
# Load the configuration settings from the config.py file
app.config.from_pyfile('config.py')
# Initialize the SQLAlchemy object with our application
db.init_app(app)
Defining our database models
In our models.py
file, we need to define our database models using SQLAlchemy’s declarative syntax. A model is a Python class that represents a table in our database and its attributes represent the columns. We also need to import the db
object which we will use to interact with our database.
For this tutorial, we will create a simple model called User
that has the following attributes:
-
id
: An integer that is the primary key of the table and uniquely identifies each user. -
name
: A string that stores the name of the user. -
email
: A string that stores the email address of the user.
Now, we can define our model as follows:
from db import db
# Define a User model that inherits from db.Model
class User(db.Model):
# Define the id attribute as an integer column that is the primary key
id = db.Column(db.Integer, primary_key=True)
# Define the name attribute as a string column that is not nullable
name = db.Column(db.String(80), nullable=False)
# Define the email attribute as a string column that is unique and not nullable
email = db.Column(db.String(120), unique=True, nullable=False)
# Define a __repr__ method that returns a string representation of the user object
def __repr__(self):
return f'<User {self.name}>'
Creating our database
After defining our database model, the next step is to create the actual database and its tables. This can be done using SQLAlchemy's create_all
method. To do this, we'll first need to activate our virtual environment (if it isn't already), and then create a file called create_db.py
.
Inside create_db.py
, we'll include the following code snippet:
from app import app, db
# Create and push an application context
with app.app_context():
# Now you can use the db object
db.create_all()
- Execute the file in the terminal.
We can verify that our database and its tables have been created by looking at the app.db file in our project directory. We can also use a tool like DB Browser for SQLite (https://sqlitebrowser.org/) to inspect and manipulate our database.
Building Our API
Now that we have created our database and its model, we can start building our API. We will use Flask’s built-in routing system to define different endpoints for our API and handle different HTTP methods. We will also use Flask’s request and jsonify functions to parse and return JSON data.
We will implement the following endpoints for our API:
GET
/users
: Return a list of all users in JSON format.GET
/users/<id>
: Return a single user with the given id in JSON format. If no user with that id exists, return a 404 (Not Found) error.POST
/users
: Create a new user with the data provided in the request body in JSON format. Return the created user in JSON format with a 201 (Created) status code.PUT
/users/<id>
: Update an existing user with the given id with the data provided in the request body in JSON format. Return the updated user in JSON format with a 200 (OK) status code. If no user with that id exists, return a 404 (Not Found) error.DELETE
/users/<id>
: Delete an existing user with the given id. Return a 204 (No Content) status code. If no user with that id exists, return a 404 (Not Found) error.
We can add the following code to our app.py file to implement these endpoints:
from flask import Flask, jsonify, request
from db import db
# Create a Flask application with the name of the current module
app = Flask(__name__)
# Load the configuration settings from the config.py file
app.config.from_pyfile('config.py')
# Initialize the SQLAlchemy object with our application
db.init_app(app)
# Import the User model from models.py
from models import User
# Define a route for the GET /users endpoint
@app.route('/users', methods=['GET'])
def get_users():
# Query all users from the database
users = User.query.all()
# Convert each user object to a dictionary
users_dict = [user.__dict__ for user in users]
# Remove the _sa_instance_state attribute from each dictionary
for user_dict in users_dict:
user_dict.pop('_sa_instance_state')
# Return a JSON response with the list of users
return jsonify(users_dict)
# Define a route for the GET /users/<id> endpoint
@app.route('/users/<int:id>', methods=['GET'])
def get_user(id):
# Query a user by id from the database
user = User.query.get(id)
# Check if the user exists
if user is None:
# Return a 404 error if not found
return jsonify({'message': 'User not found'}), 404
else:
# Convert the user object to a dictionary
user_dict = user.__dict__
# Remove the _sa_instance_state attribute from the dictionary
user_dict.pop('_sa_instance_state')
# Return a JSON response with the user data
return jsonify(user_dict)
# Define a route for the POST /users endpoint
@app.route('/users', methods=['POST'])
def create_user():
# Get the data from the request body as a dictionary
data = request.get_json()
# Check if the data is valid
if 'name' not in data or 'email' not in data:
# Return a 400 error if missing name or email
return jsonify({'message': 'Name and email are required'}), 400
else:
# Create a new user object with the data
user = User(name=data['name'], email=data['email'])
# Add and commit the user to the database
db.session.add(user)
db.session.commit()
# Convert the user object to a dictionary
user_dict = user.__dict__
# Remove the _sa_instance_state attribute from the dictionary
user_dict.pop('_sa_instance_state')
# Return a JSON response with the created user data and a 201 status code
return jsonify(user_dict), 201
# Define a route for the PUT /users/<id> endpoint
@app.route('/users/<int:id>', methods=['PUT'])
def update_user(id):
# Query a user by id from the database
user = User.query.get(id)
# Check if the user exists
if user is None:
# Return a 404 error if not found
return jsonify({'message': 'User not found'}), 404
else:
# Get the data from the request body as a dictionary
data = request.get_json()
# Check if the data is valid
if 'name' not in data or 'email' not in data:
# Return a 400 error if missing name or email
return jsonify({'message': 'Name and email are required'}), 400
else:
# Update the user object with the data
user.name = data['name']
user.email = data['email']
# Commit the changes to the database
db.session.commit()
# Convert the user object to a dictionary
user_dict = user.__dict__
# Remove the _sa_instance_state attribute from the dictionary
user_dict.pop('_sa_instance_state')
# Return a JSON response with the updated user data and a 200 status code
return jsonify(user_dict), 200
# Define a route for the DELETE /users/<id> endpoint
@app.route('/users/<int:id>', methods=['DELETE'])
def delete_user(id):
# Query a user by id from the database
user = User.query.get(id)
# Check if the user exists
if user is None:
# Return a 404 error if not found
return jsonify({'message': 'User not found'}), 404
else:
# Delete the user from the database
db.session.delete(user)
db.session.commit()
# Return a 204 status code with no response body
return '', 204
if __name__ == '__main__':
app.run()
Testing our API
Now that we have implemented our API endpoints, we can test them using a tool like Postman (https://www.postman.com/) or curl (https://curl.se/). We can use these tools to send different HTTP requests to our API and inspect the responses.
To test our API, we need to do the following steps:
- Run our Flask application using
python app.py
. - Open Postman or curl and send different requests to our API endpoints.
- Check the status codes and response bodies of each request.
First let's create new users
using POST method:
- POST
/users
: Create a new user with the data provided in the request body in JSON format. Return the created user in JSON format with a 201 (Created) status code.
Request:
#User 1:
curl -X POST -H "Content-Type: application/json" -d '{"name": "Alice","email":"alice@example.com"}' http://127.0.0.1:5000/users
Response:
{}
User 2:
curl -X POST -H "Content-Type: application/json" -d '{"name": "Bob", "email": "bob@example.com"}' http://127.0.0.1:5000/users
User 3:
curl -X POST -H "Content-Type: application/json" -d '{"name": "Charlie","email": "charlie@example.com"}' http://127.0.0.1:5000/users
Now we will use GET method to return the list of users in JSON format.
- GET
/users
:
Request:
curl http://127.0.0.1:5000/users
Response:
[
{
"id": 1,
"name": "Alice",
"email": "alice@example.com"
},
{
"id": 2,
"name": "Bob",
"email": "bob@example.com"
},
{
"id": 3,
"name": "Charlie",
"email": "charlie@example.com"
}
]
- GET
/users/<id>
: Return a single user with the given id in JSON format. If no user with that id exists, return a 404 (Not Found) error.
Request:
curl http://localhost:5000/users/1
Response:
{
"id": 1,
"name": "Alice",
"email": "alice@example.com"
}
Request:
curl http://localhost:5000/users/4
Response:
{
"message": "User not found"
}
- PUT
/users/<id>
: Update an existing user with the given id with the data provided in the request body in JSON format. Return the updated user in JSON format with a 200 (OK) status code. If no user with that id exists, return a 404 (Not Found) error.
Request:
curl -X PUT -H "Content-Type: application/json" -d '{"name": "Alice", "email": "alice@new.com"}' http://localhost:5000/users/1
Response:
{}
Use GET to check the updated details of the user
curl http://localhost:5000/users/1
Response:
{
"id": 1,
"name": "Alice",
"email": "alice@new.com"
}
Request:
curl -X PUT -H "Content-Type: application/json" -d '{"name": "Eve", "email": "eve@example.com"}' http://localhost:5000/users/4
Response:
{
"message": "User not found"
}
- DELETE
/users/<id>
: Delete an existing user with the given id. Return a 204 (No Content) status code. If no user with that id exists, return a 404 (Not Found) error.
Request:
curl -X DELETE http://localhost:5000/users/2
Response:
No response body.
Request:
curl -X DELETE http://localhost:5000/users/5
Response:
{
"message": "User not found"
}
To sum up, in this tutorial we have learned how to create a simple RESTful API using Flask and SQLAlchemy. We have also learned how to perform CRUD operations on our database using Python objects and how to test our API using curl.
Flask and Flask-RESTful provide many advanced features and options for building REST APIs such as connecting to databases using flask_sqlalchemy
, serialization and deserialization of data using flask_marshmallow
, adding authentication and authorization using flask_jwt_extended
, and generating interactive documentation for the API using flask_swagger_ui
.
For more information and examples, you can refer to the official documentation of Flask and Flask-RESTful available at:
I hope you enjoyed this tutorial and found it useful. Happy coding!
This content originally appeared on DEV Community and was authored by Krishnanshu Rathore
Krishnanshu Rathore | Sciencx (2023-03-28T01:24:51+00:00) How to build an API using Flask. Retrieved from https://www.scien.cx/2023/03/28/how-to-build-an-api-using-flask/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.