Application Architecture
Resource Layer (Flask-RESTful Resources)
The Resource layer maps HTTP endpoints to Python classes. Each Resource encapsulates request parsing, authentication, and CRUD logic. Flask-RESTful handles routing and serialization.
📚 Book Resource
The Book resource manages individual books and the book list. It uses a shared parser for input validation and JWT for secure reads.
from flask_restful import Resource, reqparse
from flask_jwt import jwt_required
from models.book import BookModel
class Book(Resource):
parser = reqparse.RequestParser()
parser.add_argument('price',
type=float, required=True, help="This field cannot be left blank!")
parser.add_argument('store_id',
type=int, required=True, help="Every item needs a store_id.")
parser.add_argument('author',
type=str, required=True, help="Every item needs a author.")
parser.add_argument('isbn',
type=str, required=True, help="Every item needs a isbn.")
parser.add_argument('release_date',
type=str, required=False)
...
Derived from the source code .
Parser Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
| price | float | Yes | Price of the book. |
| store_id | int | Yes | Foreign key to the owning Store. |
| author | str | Yes | Book author’s name. |
| isbn | str | Yes | International Standard Book Number. |
| release_date | str | No | Publication date (YYYY-MM-DD). |
Resource Methods
| Method | Description |
|---|---|
@jwt_required()get(title) |
Return book JSON or 404 if not found. |
| post(title) | Create new book after validating uniqueness; return 201 or 400/500 on failure. |
| delete(title) | Remove book by title; return success or 404. |
| put(title) | Update price if exists, else create a new book; return book JSON. |
| BookList.get() | Return {'books': [...]} array of all BookModel JSON. |
Source: resources/book.py .
GET /book/{title}
{
"title": "Retrieve a Book",
"description": "Fetch a single book by title. Requires JWT authentication.",
"method": "GET",
"baseUrl": "http://localhost:5000",
"endpoint": "/book/{title}",
"headers": [
{ "key": "Authorization", "value": "JWT <token>", "required": true }
],
"pathParams": [
{ "key": "title", "value": "Book title", "required": true }
],
"bodyType": "none",
"responses": {
"200": {
"description": "Book found",
"body": "{\n \"title\": \"Kalinga\",\n \"price\": 200.0,\n \"author\": \"sapan mohanty\",\n \"isbn\": \"WER3455\",\n \"release_date\": \"2020-12-12\"\n}"
},
"404": {
"description": "Book not found",
"body": "{ \"message\": \"book not found\" }"
}
}
}
POST /book/{title}
{
"title": "Create a Book",
"description": "Add a new book with given title. Validates uniqueness.",
"method": "POST",
"baseUrl": "http://localhost:5000",
"endpoint": "/book/{title}",
"headers": [
{ "key": "Authorization", "value": "JWT <token>", "required": true },
{ "key": "Content-Type", "value": "application/json", "required": true }
],
"pathParams": [
{ "key": "title", "value": "Book title", "required": true }
],
"bodyType": "json",
"requestBody": "{\n \"price\": 19.99,\n \"store_id\": 1,\n \"author\": \"Jane Doe\",\n \"isbn\": \"ABC123\",\n \"release_date\": \"2021-01-01\"\n}",
"responses": {
"201": {
"description": "Book created",
"body": "{ \"title\": \"NewBook\", \"price\": 19.99, \"author\": \"Jane Doe\", \"isbn\": \"ABC123\", \"release_date\": \"2021-01-01\" }"
},
"400": {
"description": "Duplicate title",
"body": "{ \"message\": \"An book with title 'NewBook' already exists.\" }"
},
"500": {
"description": "Insertion error",
"body": "{ \"message\": \"An error occurred inserting the book.\" }"
}
}
}
DELETE /book/{title}
{
"title": "Delete a Book",
"description": "Remove a book by title.",
"method": "DELETE",
"baseUrl": "http://localhost:5000",
"endpoint": "/book/{title}",
"headers": [],
"pathParams": [
{ "key": "title", "value": "Book title", "required": true }
],
"bodyType": "none",
"responses": {
"200": {
"description": "Deletion succeeded",
"body": "{ \"message\": \"Item deleted.\" }"
},
"404": {
"description": "Item not found",
"body": "{ \"message\": \"Item not found.\" }"
}
}
}
PUT /book/{title}
{
"title": "Upsert a Book",
"description": "Update price or create a new book if absent.",
"method": "PUT",
"baseUrl": "http://localhost:5000",
"endpoint": "/book/{title}",
"headers": [
{ "key": "Content-Type", "value": "application/json", "required": true }
],
"pathParams": [
{ "key": "title", "value": "Book title", "required": true }
],
"bodyType": "json",
"requestBody": "{\n \"price\": 25.00,\n \"store_id\": 2,\n \"author\": \"John Smith\",\n \"isbn\": \"XYZ789\",\n \"release_date\": \"2022-05-05\"\n}",
"responses": {
"200": {
"description": "Book upserted",
"body": "{ \"title\": \"Title\", \"price\": 25.0, \"author\": \"John Smith\", \"isbn\": \"XYZ789\", \"release_date\": \"2022-05-05\" }"
}
}
}
GET /books
{
"title": "List All Books",
"description": "Fetch all books in the library.",
"method": "GET",
"baseUrl": "http://localhost:5000",
"endpoint": "/books",
"headers": [],
"bodyType": "none",
"responses": {
"200": {
"description": "List of books",
"body": "{ \"books\": [ { ... }, { ... } ] }"
}
}
}
🏬 Store Resource
The Store resource handles CRUD for bookstores. It returns store details and associated books dynamically.
from flask_restful import Resource
from models.store import StoreModel
class Store(Resource):
def get(self, name):
store = StoreModel.find_by_name(name)
if store:
return store.json()
return {'message': 'Store not found'}, 404
...
class StoreList(Resource):
def get(self):
return {'stores': [s.json() for s in StoreModel.query.all()]}
Derived from resources/store.py .
Endpoints Summary
| Endpoint | Method | Description |
|---|---|---|
/store/{name} |
GET | Retrieve store with its books. |
/store/{name} |
POST | Create new store; return 400/500 on error. |
/store/{name} |
DELETE | Delete store; no content on success. |
/stores |
GET | List all stores. |
GET /store/{name}
{
"title": "Retrieve a Store",
"description": "Fetch a store by name.",
"method": "GET",
"baseUrl": "http://localhost:5000",
"endpoint": "/store/{name}",
"headers": [],
"pathParams": [
{ "key": "name", "value": "Store name", "required": true }
],
"bodyType": "none",
"responses": {
"200": {
"description": "Store found",
"body": "{ \"name\": \"MyStore\", \"books\": [ ... ] }"
},
"404": {
"description": "Store not found",
"body": "{ \"message\": \"Store not found\" }"
}
}
}
POST /store/{name}
{
"title": "Create a Store",
"description": "Add a new store by name.",
"method": "POST",
"baseUrl": "http://localhost:5000",
"endpoint": "/store/{name}",
"headers": [],
"pathParams": [
{ "key": "name", "value": "Store name", "required": true }
],
"bodyType": "none",
"responses": {
"201": {
"description": "Store created",
"body": "{ \"name\": \"NewStore\", \"books\": [] }"
},
"400": {
"description": "Duplicate name",
"body": "{ \"message\": \"A store with name 'NewStore' already exists.\" }"
},
"500": {
"description": "Creation error",
"body": "{ \"message\": \"An error occurred creating the store.\" }"
}
}
}
DELETE /store/{name}
{
"title": "Delete a Store",
"description": "Remove store by name.",
"method": "DELETE",
"baseUrl": "http://localhost:5000",
"endpoint": "/store/{name}",
"headers": [],
"pathParams": [
{ "key": "name", "value": "Store name", "required": true }
],
"bodyType": "none",
"responses": {
"200": {
"description": "Store deleted",
"body": "{ \"message\": \"Store deleted\" }"
}
}
}
GET /stores
{
"title": "List All Stores",
"description": "Fetch all stores with books.",
"method": "GET",
"baseUrl": "http://localhost:5000",
"endpoint": "/stores",
"headers": [],
"bodyType": "none",
"responses": {
"200": {
"description": "List of stores",
"body": "{ \"stores\": [ { ... }, { ... } ] }"
}
}
}
👤 User Registration Resource
The UserRegister resource handles new user signup. It validates input and stores credentials.
from flask_restful import Resource, reqparse
from models.user import UserModel
class UserRegister(Resource):
parser = reqparse.RequestParser()
parser.add_argument('username',
type=str, required=True, help="This field cannot be blank.")
parser.add_argument('password',
type=str, required=True, help="This field cannot be blank.")
def post(self):
data = UserRegister.parser.parse_args()
if UserModel.find_by_username(data['username']):
return {"message": "A user with that username already exists"}, 400
user = UserModel(data['username'], data['password'])
user.save_to_db()
return {"message": "User created successfully."}, 201
Derived from resources/user.py .
POST /register
{
"title": "Register a User",
"description": "Create a new user account.",
"method": "POST",
"baseUrl": "http://localhost:5000",
"endpoint": "/register",
"headers": [
{ "key": "Content-Type", "value": "application/json", "required": true }
],
"bodyType": "json",
"requestBody": "{\n \"username\": \"newuser\",\n \"password\": \"securepass\"\n}",
"responses": {
"201": {
"description": "Registration succeeded",
"body": "{ \"message\": \"User created successfully.\" }"
},
"400": {
"description": "Username exists",
"body": "{ \"message\": \"A user with that username already exists\" }"
}
}
}
{
"title": "Key Takeaway",
"content": "Resource classes cleanly separate request handling from business logic."
}