Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 22

Building RESTful APIs with Flask-RESTful and CRUD Operations

Recommended resources

Hello! Welcome back to your course on becoming a Full Stack AI Developer.

In our previous lesson, we covered the theoretical foundations of RESTful APIs, including resources, HTTP verbs, and status codes. You learned why APIs are designed this way. Now, it's time to put that theory into practice.

Today's learning outcome is to build a RESTful API supporting CRUD operations using Flask-RESTful. This is a critical skill. When you build an AI model, you'll need an API to make its predictions available to other applications, like a web front-end or a mobile app. This lesson is your first major step in learning how to do that.

Python Flask REST API with CRUD Operations
This image illustrates our goal for today: using Python and Flask to build a RESTful API that handles Create, Read, Update, and Delete (CRUD) operations.

We'll be using Flask-RESTful, an extension for Flask that simplifies API development by providing tools that align perfectly with the REST principles we discussed.

1. Project Setup and Database Model

Before we can manage data through an API, we need a place to store that data. We will use Flask-SQLAlchemy to set up a simple SQLite database and define a data model for our "users" resource. We will then set up our basic Flask application and initialize the Flask-RESTful extension.

A well-structured API starts with a clear plan. The diagram below shows the high-level design of the kind of application we are building. An API client (like a browser or testing tool) will send HTTP requests to our Flask backend, which will then use SQLAlchemy to interact with a database.

APIs using Flask & SQLAlchemy High-Level Design
This diagram shows the architecture of our application. The API consumer sends requests (GET, POST, etc.) to the Flask API service, which uses SQLAlchemy as an ORM to perform operations on the database.

To get started with the practical setup, we'll watch a video that guides us through the initial steps.

Python REST API Tutorial for Beginners | How to Build a Flask REST API

This video by Dave Gray provides a clear, step-by-step guide to setting up a Flask project for a REST API. We'll start by setting up the environment and creating our database model.

Watch the video from the beginning until 10:01 to understand the project setup. Then, watch from 12:33 to 17:02 to see how the database model is defined and the database is created. Pay close attention to: Creating a virtual environment and installing Flask, Flask-RESTful, and Flask-SQLAlchemy. Setting up the basic app.py file. Defining the UserModel class using db.Model. Creating the database using a separate script.

To summarize the key code from the video, your initial api.py (or app.py) file will look something like this:




# api.py
from flask import Flask
from flask_restful import Api
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
api = Api(app)




# Configure the SQLite database
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.db'
db = SQLAlchemy(app)




# Define the User model
class UserModel(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(80), unique=True, nullable=False)

    def __repr__(self):
        return f"<User {self.name}>"




# This block is important for creating the database tables
# It should be run once. After the first run, you can comment it out.
# with app.app_context():
#    db.create_all()

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

Note: The video uses a separate create_db.py script. An alternative, shown above, is to use with app.app_context(): db.create_all() directly in your main file. Both achieve the same result: creating the database.db file based on your models.


2. Building Resources for CRUD Operations

In Flask-RESTful, the core building blocks are Resources. A resource is a Python class that maps to one or more API endpoints and contains methods for each supported HTTP verb (get, post, put, etc.).

We will build two types of resources for our users entity:

  1. A collection resource (Users): To handle requests on the collection of all users (e.g., GET /api/users to list all users, POST /api/users to create a new user).
  2. An individual resource (User): To handle requests for a single, specific user (e.g., GET /api/users/1 to get user 1, PATCH /api/users/1 to update them).

Implementing GET (All) and POST (Create)

Let's start with the collection resource. We'll implement the GET method to read all users and the POST method to create a new user.

To do this effectively, we'll use two powerful features of Flask-RESTful:

  • reqparse: To parse and validate incoming request data (for our POST method).
  • marshal_with: A decorator to format and serialize the data we send back in our response.

Python REST API Tutorial for Beginners | How to Build a Flask REST API

Continuing with the same video, let's see how to implement our first resource to handle GET and POST requests.

Watch the video from 19:56 to 26:22. This segment covers: Creating the Users resource class. Implementing the get method to retrieve all users. Defining the user_fields for serialization with @marshal_with. Setting up reqparse (called user_args) for input validation. Implementing the post method to create a new user and add it to the database.

After following the video, your api.py will be expanded with the resource for handling the collection of users:




# ... (imports and app setup from before) ...
from flask_restful import Resource, reqparse, fields, marshal_with




# ... (db and UserModel definition) ...




# Define the structure of the data to be rendered as JSON
user_fields = {
    'id': fields.Integer,
    'name': fields.String,
    'email': fields.String
}




# Setup the request parser for POST requests
parser = reqparse.RequestParser()
parser.add_argument('name', type=str, required=True, help='Name cannot be blank!')
parser.add_argument('email', type=str, required=True, help='Email cannot be blank!')




# Resource for a collection of users (GET all, POST new)
class UserListResource(Resource):
    @marshal_with(user_fields)
    def get(self):
        users = UserModel.query.all()
        return users, 200

    @marshal_with(user_fields)
    def post(self):
        args = parser.parse_args()
        user = UserModel(name=args['name'], email=args['email'])
        db.session.add(user)
        db.session.commit()



        # The created user object is returned and marshalled
        return user, 201




# Add the resource to the API at the '/api/users' endpoint
api.add_resource(UserListResource, '/api/users')

At this point, you have functional GET and POST endpoints. You can test them using a tool like Postman or the Thunder Client extension for VS Code, as demonstrated in the next part of the video (26:22 to 30:29).


3. Implementing GET (One), Update, and Delete

Now let's create the second resource to manage individual users. This resource will handle endpoints that include a user's ID in the URL, like /api/users/1.

We will implement:

  • GET /api/users/<id>: To retrieve a single user.
  • PATCH /api/users/<id>: To update a user's details. PATCH is used for partial updates, while PUT is typically for a full replacement.
  • DELETE /api/users/<id>: To remove a user.

Python REST API Tutorial for Beginners | How to Build a Flask REST API

Let's complete our CRUD functionality by building the resource for individual user operations.

Watch the video from 30:29 to 38:48. This final practical part demonstrates: Creating a new User resource class for single items. Passing the user_id from the URL to the resource methods. Implementing the get, patch, and delete methods. Using abort to handle cases where a user is not found (404 error). Testing the new PATCH and DELETE endpoints.

By adding this final piece, your API becomes fully-featured for user management. Here's the code for the individual user resource:




# ... (at the end of your api.py file) ...




# Setup a separate parser for PATCH requests, where fields are not required
update_parser = reqparse.RequestParser()
update_parser.add_argument('name', type=str, help='Name to update')
update_parser.add_argument('email', type=str, help='Email to update')




# Resource for a single user (GET one, PATCH, DELETE)
class UserResource(Resource):
    @marshal_with(user_fields)
    def get(self, user_id):
        user = UserModel.query.get_or_404(user_id)
        return user, 200

    @marshal_with(user_fields)
    def patch(self, user_id):
        user = UserModel.query.get_or_404(user_id)
        args = update_parser.parse_args()

        if args['name']:
            user.name = args['name']
        if args['email']:
            user.email = args['email']
        
        db.session.commit()
        return user, 200

    def delete(self, user_id):
        user = UserModel.query.get_or_404(user_id)
        db.session.delete(user)
        db.session.commit()
        return {'message': 'User deleted'}, 204 # 204 No Content




# Add the second resource to the API
# Note the URL now includes an integer parameter for the user_id
api.add_resource(UserResource, '/api/users/<int:user_id>')

A quick note: The video uses .query.filter_by(id=id).first() and then checks if the result is None before aborting. A convenient Flask-SQLAlchemy shortcut is query.get_or_404(id), which automatically finds the user by primary key or triggers a 404 Not Found response.


Conclusion

Congratulations! You have successfully built a complete RESTful API with full CRUD functionality using Flask-RESTful. You've gone from the theory of REST to a practical, working implementation.

Key Takeaways:

  • Flask-RESTful provides a structured way to build APIs using Resource classes.
  • It's common practice to have separate resources for handling collections (e.g., /users) and individual items (e.g., /users/<id>).
  • reqparse is a powerful tool for validating incoming request data, ensuring your API is robust.
  • The @marshal_with decorator simplifies output formatting, guaranteeing your API returns consistent, well-structured JSON responses.
  • HTTP verbs (GET, POST, PATCH, DELETE) map directly to methods within your resource classes.

You now have a solid foundation for exposing services and data over the web. In our next lesson, we will build upon this API. We'll address a crucial real-world problem: security. You will learn how to configure CORS and implement JWT-based API authentication to protect your endpoints and control who can access them.