Blogopedia: Relational Modeling & Role-Based Publishing Platform
A full-stack publication system developed during undergraduate coursework to explore relational schema normalization, session-based authentication lifecycles, decorator-driven authorization, and server-side Jinja2 template rendering with Flask and SQLAlchemy.

Understanding Server-Side Web Fundamentals from First Principles
In contemporary frontend-heavy ecosystems, developers frequently rely on abstracted Backend-as-a-Service (BaaS) platforms without deeply understanding how session cookies, cryptographic salt hashing, or relational foreign key cascades operate under the hood.
Blogopedia was developed as an academic capstone to master these core backend mechanics from scratch:
- Relational Integrity & Foreign Keys: Defining parent-child entity constraints across users, blog posts, and nested comments.
- Session Authentication & Cryptographic Hashing: Implementing salted PBKDF2 hashing, secure cookie serialization, and session restoration via Flask-Login.
- Form Validation & CSRF Mitigation: Validating client input on the server and binding cryptographic CSRF tokens to every mutating POST request.
- Route-Level Access Control: Constructing custom Python function decorators to enforce role hierarchies (e.g. distinguishing post authors from system administrators).
Normalized SQLAlchemy Relational Schema
The database model establishes strict referential integrity across three core entities using SQLAlchemy ORM: User, BlogPost, and Comment.
- id: Integer (PK)
- email: String(100) (Unique)
- password: String(250) (Hashed)
- name: String(100)
- → posts (1:M relationship)
- → comments (1:M relationship)
- id: Integer (PK)
- author_id: FK → users.id
- title: String(250) (Unique)
- subtitle: String(250)
- date: String(250)
- body: Text (CKEditor HTML)
- → comments (1:M relationship)
- id: Integer (PK)
- author_id: FK → users.id
- post_id: FK → blog_posts.id
- text: Text (Sanitized Body)
- Both FKs enforce cascading referential integrity.
┌───────────────────────────┐
│ User │
├───────────────────────────┤
│ id (PK) │
│ email (Unique) │
│ password (PBKDF2 Hashed) │
│ name │
└─────────────┬─────────────┘
│ 1
│
│ has many
├──────────────────────────────────────────┐
│ M │ M
▼ ▼
┌───────────────────────────┐ ┌───────────────────────────┐
│ BlogPost │ │ Comment │
├───────────────────────────┤ ├───────────────────────────┤
│ id (PK) │ │ id (PK) │
│ author_id (FK -> User.id) │ 1 │ author_id (FK -> User.id) │
│ title │──has many───>│ post_id (FK -> Post.id) │
│ subtitle / date / body │ M │ text │
└───────────────────────────┘ └───────────────────────────┘PBKDF2 Password Hashing & Custom Admin Decorators
Password Storage & Verification
Passwords are never stored in plaintext. Utilizing Werkzeug's security suite, user credentials pass through generate_password_hash(method='pbkdf2:sha256', salt_length=8). During login, check_password_hash verifies user input against stored salt-hash pairs.
The @admin_only Route Decorator
Rather than sprinkling imperative role checks across route handlers, an idiomatic Python decorator (@admin_only) inspects current_user.id == 1. If an unprivileged user attempts to access post creation, edit, or delete endpoints, the decorator intercepts execution and issues an immediate 403 Forbidden.
CSRF Protection with Flask-WTF
Every client form (registration, login, post editing, comment submission) is modeled using WTForms classes. Flask-WTF injects a cryptographic CSRF token that is validated on form submission, protecting the session cookie from cross-site request forgery attacks.
Serverless WSGI Adaptability
The repository includes a vercel.json deployment configuration using the @vercel/python runtime. It maps the Flask WSGI callable (app in main.py) to serverless function invocations without modifying business logic.
Architectural Choices for a Coursework Capstone
When evaluating Blogopedia within an engineering portfolio, it is vital to assess the intentional choices made during its implementation:
SSR eliminated state synchronization issues, simplified authentication cookie handling, and delivered instantaneous page loads with zero JavaScript bundle overhead.
Abstracted database interactions allowed seamless switching between SQLite for zero-config local development and PostgreSQL for production environments via environment variables.
Instead of maintaining file upload endpoints and object storage buckets, MD5-hashed email addresses query Gravatar to display author avatars dynamically.
Limitations of the Coursework Architecture
While robust for educational purposes, the system reflects the boundaries of a foundational capstone:
The @admin_only decorator inspects current_user.id == 1. In an enterprise system, this would be replaced with dynamic role tables, permission claims, or group memberships.
All requests, database queries, and template renders run synchronously in a single Python thread per worker. High concurrent write throughput would require connection pooling and async workers.
User sessions rely on signed client cookies. Horizontal scaling across serverless instances requires configuring a shared secret key and maintaining lightweight session payloads.
Blog post bodies rely on CKEditor rich text HTML rendering with Jinja2 safe filters. While effective for personal blogging, multi-tenant public input requires rigorous server-side HTML sanitization (e.g. Bleach).