Now that we have a solid understanding of the REST API architecture, it is time to start implementing one on a project. In the Overview article, you saw the end project, fully built with API developed for it. However, to help you learn how it was built, we will start from the bottom. I have already created a basic chat application that you can refer to as you go through this article. To compare your work with the article's final code, refer to v3.0.0-api-blueprint release on GitHub.
For your reference, these are the topics in our discussion:
- Overview
- API Blueprint (this article)
- Resource Representation
- Error Handling
- Unique Resource Identifiers
- API Authentication
- API-friendly Error Messages
- API Testing Using Postman
- API Testing And Documentation Using API Fairy
This article is broken down into the following subsections:
In the linked basic chat application (v1.0.0-basic-app above), no blueprints have been used. A blueprint is a logical structure that represents a subset of an application. For example, instead of bundling everything in routes.py
, we can choose to separate the logic into auth for authentication-related features or errors for error-related features. Check out the _v2.0.0-blueprint release. Why use a blueprint? Blueprints are fantastic for larger applications. Should I ever want to scale this application, a new project structure utilizing blueprints would be suitable.
To create an api blueprint, let us start by creating an empty folder called api in the app/ sub-directory.
(venv)$ mkdir app/api
# I am running this command from the application's root directory
Create a new file called __init__.py
in the new directory:
(venv)$ touch app/api/__init__.py
Update this new file with the following initialization:
# app/api/__init__.py: Initialize the API blueprint
from flask import Blueprint
bp = Blueprint('api', __name__)
from app.api import users, errors, tokens, posts
The last line requires us to create the four files (also known as modules -- they do not exist yet). They are imported at the bottom to avoid circular dependency errors.
(venv)$ cd app
(venv)app$ touch users errors tokens posts
The gist of our API is going to be in the app/api/users.py and app/api/posts.py modules.
To begin with the _app/api/users.py_
module, these are the endpoints we are going to create and work with:
HTTP Method | Resource URL | Description |
---|---|---|
GET | /api/users/user_id | Return a user based on their id |
GET | /api/users | Return a collection of users |
POST | /api/users | Create a new user account |
PUT | /api/users/user_id | Modify a user of id |
The API endpoints for the users are going to be as follows (skeleton):
# app/api/users.py: User API resources
from app.api import bp
@bp.route('/users/<int:id>', methods=['GET'])
def get_user(id):
pass
@bp.route('/users', methods=['GET'])
def get_users():
pass
@bp.route('/users', methods=['POST'])
def create_user():
pass
@bp.route('/users/<int:id>/', methods=['PUT'])
def update_user(id):
pass
The endpoints that we are going to work with on the app/api/posts.py module are as follows:
HTTP Method | Resource URL | Description |
---|---|---|
GET | /api/posts/post_id | Return a post based on its id |
GET | /api/posts | Return a collection of posts |
POST | /api/posts | Create a new post |
PUT | /api/posts/post_id | Modify the post of id |
The API endpoints for the posts are going to be as follows (skeleton):
# app/api/posts.py: Posts API resources
from app.api import bp
@bp.route('/posts/<int:id>', methods=['GET'])
def get_post(id):
pass
@bp.route('/posts', methods=['GET'])
def get_posts():
pass
@bp.route('/posts', methods=['POST'])
def create_post():
pass
@bp.route('/posts/<int:id>', methods=['PUT'])
def update_post(id):
pass
@bp.route('/posts/<int:id>', methods=['DELETE'])
def update_post(id):
pass
Something about the posts APIs is that the posts have to be linked to a user. For example, in the POST method (while working with posts APIs), we need to ensure that a post is made by a logged-in user identified by their id
. This post will be associated with them. Similarly, when interacting with a post (say to update or delete one), a logged-in user can only do so to posts they have authored.
We can define a few helper functions to deal with error responses. To begin, let us add the following:
# app/api/errors.py: Deal with error responses
def bad_request():
pass
This is where the authentication subsystem is going to be defined. We will provide an alternative way for clients who are not web browsers to log in.
# app/api/tokens.py: API authentication module
def get_token():
pass
def revoke_token():
pass
Finally, to complete the setup process, we can now register the API blueprint with the application factory function:
# app/__init__.py: Register API blueprint with factory function
def create_app(config_class=Config):
app = Flask(__name__)
# ...
from app.api import bp as api_bp
app.register_blueprint(api_bp, url_prefix='/api')
# ...