Building a Robust API with Flask, flask_restful, and flask_sqlalchemy in Python

Python's Flask framework offers a powerful way to create web applications and APIs effortlessly. When combined with flask_restful for API creation and flask_sqlalchemy for database management, it becomes a robust toolkit for developing RESTful APIs with ease.

This article is a continuation of my previous article, Building APIs with Flask and flask_restful in Python, where I explained how to use flask_restful in a Python application. So, I will not repeat the same here. Let's continue from the previous article.

Install the required packages using pip.

pip install Flask flask_restful flask_sqlalchemy

Initializing Flask and Creating API Endpoints

Let's create a simple Flask application and define an API using flask_restful.

from flask import Flask
from flask_restful import Api, Resource, reqparse, abort, fields, marshal_with
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
api = Api(app)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.db'
db = SQLAlchemy(app)

class UserModel(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(100), nullable=False)
    name = db.Column(db.String(100), nullable=False)
    age = db.Column(db.Integer, nullable=False)
    gender = db.Column(db.String, nullable=False)

    def __repr__(self):
        return f"User(email = {email}, name = {name}, age = {age}, gender = {gender})"

# db.create_all() - This will be executed only once when the db needs to be initialised. It should be commented post that

users_put_args = reqparse.RequestParser()
users_put_args.add_argument(
    "email", type=str, help="Email is required", required=True)
users_put_args.add_argument(
    "name", type=str, help="Name is required", required=True)
users_put_args.add_argument(
    "age", type=int, help="Age is required", required=True)
users_put_args.add_argument(
    "gender", type=str, help="Gender is required", required=True)


def abort_if_user_already_exists(user_id):
    result = UserModel.query.filter_by(id=user_id).first()
    if result:
        abort(409, message="This user already exists..")


def abort_if_user_does_not_exists(user_id):
    result = UserModel.query.filter_by(id=user_id).first()
    if not result:
        abort(404, message="User does not exist..")


resource_fields = {
    'id': fields.Integer,
    'email': fields.String,
    'name': fields.String,
    'age': fields.Integer,
    'gender': fields.String
}


class Users(Resource):
    @marshal_with(resource_fields)
    def get(self, user_id):
        abort_if_user_does_not_exists(user_id)
        result = UserModel.query.filter_by(id=user_id).first()
        return result, 200

    @marshal_with(resource_fields)
    def put(self, user_id):
        abort_if_user_already_exists(user_id)
        args = users_put_args.parse_args()
        user = UserModel(
            id=user_id, email=args['email'], name=args['name'], age=args['age'], gender=args['gender'])
        db.session.add(user)
        db.session.commit()
        return user, 201

    def delete(self, user_id):
        abort_if_user_does_not_exists(user_id)
        user = UserModel.query.filter_by(id=user_id).first()
        db.session.delete(user)
        db.session.commit()

        return "User deleted successfully", 204


api.add_resource(Users, "/api/users/<int:user_id>")


if __name__ == "__main__":
    app.run(debug=True)

Explanation of Key Concepts

  • API Creation (Api and Resource)
    flask_restful's Api class helps in creating APIs by binding resources to URLs. Resources (Resource class) are representations of the RESTful entities that respond to HTTP methods (GET, POST, PUT, DELETE).
  • Request Parsing (reqparse)
    The reqparse module provides a way to parse and validate request data. In our example, the parser is created to handle the incoming data for tasks.
  • Error Handling (abort)
    abort is used to abort requests with appropriate HTTP error codes. In our case, it returns a 404 error if a task with a specified ID is not found.
  • Serialization (fields) and Marshalling (marshal_with)
    fields define the structure of the data to be serialized, and marshal_with decorates the resource methods to apply the specified fields to the returned data. This helps in transforming database objects into JSON responses.

I also created another notebook with sample http requests to handle the methods created above. Attaching it to the post.

Conclusion

Combining Flask with flask_restful and flask_sqlalchemy provides a streamlined approach to building powerful APIs. The use of resources, request parsing, error handling, serialization, and marshaling simplifies the development process and creates scalable and maintainable APIs.

This article has covered the basics of these functionalities, providing a foundation for creating robust RESTful APIs in Python. Explore further to enhance and customize your API based on your specific project requirements.

Happy coding! 🚀


Similar Articles