Flask Part 2: Building a RESTful API

This is the second of three posts about building a JSON API with Flask.
Part 1 arrived yesterday and part 3 is arriving tomorrow.

In the previous post we learned how to serialize SQLAlchemy models to/from JSON. Now let’s use that to build a RESTful JSON API with Flask.

What is a RESTful API

A RESTfu API is a website that conforms to the REST conventions by allowing CRUD operations on resources. Resources are the noun you are fetching or updating, for ex: Users or Goals. Unlike GraphQL, where every resource shares the same url endpoint, RESTful APIs use a different url for each resource. That means Users would be at yoursite.com/api/users and Goals at yoursite.com/api/goals. When you visit yoursite.com/api/users in your browser, it should return JSON with a list of users.

A RESTful API with Flask

Here’s a simple Flask API that always returns an empty list of Users:

from flask import Flask
app = Flask(__name__)

@app.route("/api/users")
def users():
    return "[]"

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

Let’s improve that to return a list of users from our database using SQLAlchemy.

from flask import Flask, json
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
db = SQLAlchemy(app)

class User(BaseModel):
    id = db.Column(UUID(), primary_key=True, default=uuid.uuid4)
    username = db.Column(db.String(), nullabe=False, unique=True)
    password = db.Column(db.String())
    _default_fields = ["username"]
    _hidden_fields = ["password"]

@app.route("/api/users")
def users():
    return json.dumps([user.to_dict() for user in User.query.all()])

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

The BaseModel class is the one we created in Part 1: SQLAlchemy Models as JSON.

Visiting yoursite.com/api/users in your browser you should see:

[{"id": "488345de-88a1-4c87-9304-46a1a31c9414", "username": "zzzeek"}]

Following this pattern, we create GET resources for all our SQLAlchemy models.

Updating Models with PUT method

Most APIs support more than just GET requests. To update Users let’s add a PUT method that accepts JSON and updates a User’s attributes. This example depends on WTForms and WTForms-JSON libraries for input validation.

@app.route("/api/users/<string:user_id>", methods=['PUT'])
def users_update(user_id):
    user = User.query.get(user_id)
    form = UserForm.from_json(request.get_json())
    if not form.validate():
        return jsonify(errors=form.errors), 400
    user.from_dict(**form.data)
    db.session.commit()
    return jsonify(user=user.to_dict())

Sending {"username": "zoe"} to yoursite.com/api/users/488345de-88a1-4c87-9304-46a1a31c9414 updates User with a new username. We validate user input with WTForms and control which database columns are readable and writeable with our BaseModel SQLAlchemy class.

Conclusion

Now that we’re building APIs with Flask, it’s time to add extra features with some useful decorators and more BaseModel helper methods. Continue reading Part 3: Flask API Decorators and Helpers.

Tags in this article: