Capstone Project - Secure Blog Posts API with Role-Based Access Control (RBAC)
Problem Statement In this project, you will build a secure, production-ready Blog Posts REST API that manages users and blog posts with a one-to-many relationship using SQLAlchemy ORM. You will implement role-based authorization with two user roles:- Author: Can register, authenticate, view all posts, and create/update/delete their own posts.
- Admin: Has full access to manage any user account and delete or update any blog post on the platform.
Annotated dependency injection.
Learning Objectives
After completing this project, you will be able to:- Model one-to-many relationships in SQLAlchemy and map foreign keys.
- Structure request validation and response models using Pydantic inheritance.
- Restrict endpoint operations using JWT-based OAuth2 authentication and scopes/roles.
- Leverage modern
Annotatedsyntax to perform field validation and dependency injection. - Design database access abstraction using the Repository Pattern.
- Separate business logic cleanly into the Service Layer.
- Handle API errors globally and return uniform error structures.
Final Project Architecture
Step 1: Create the Project Directory
Objective Create the project directory and initialize the project environment usinguv.
Instructions
- Create a new project directory named
blog-rbac-api. - Navigate to the project directory.
- Initialize the project structure using
uv init.
Solution
Solution
Code
- The
blog-rbac-apifolder is created. - A
pyproject.tomlfile is created.
Step 2: Initialize Git and Create Gitignore
Objective Set up version control tracking and declare file paths to ignore. Instructions- Initialize a new git repository in the workspace root.
- Create a
.gitignorefile mapping path patterns that should not be tracked by Git.
.gitignore.
Solution
Solution
CodeInitialize Git:Create Git ActionsCommit setup:
.gitignore:git status reports .gitignore is successfully committed.
Step 3: Install Required Dependencies
Objective Install all required third-party libraries for database connection, token authentication, and the web server. Instructions- Run
uv addto install project dependencies.
Solution
Solution
CodeGit Actions
pyproject.toml shows the newly installed dependencies under the dependency section.
Step 4: Create the Modular Package Folder Structure
Objective Lay out folders and files to enforce separation of concerns across models, schemas, repositories, services, and routers. Instructions- Create the application package directory named
app. - Create folders for
core,models,schemas,repositories,services, androutersunderapp. - Place empty
__init__.pyfiles inside each folder.
Solution
Solution
CodeFolder LayoutGit ActionsCommit the folder structure:
Step 5: Configure the Database Connection
Objective Configure database connection parameters and baseline database session manager settings. Instructions- Create
app/database.pyat the root of the app package. - Initialize database engine settings, the session factory, and a base model class
Base.
Solution
Solution
CodeFile: Git ActionsCommit the changes:
app/database.pyStep 6: Create the Database Session Dependency
Objective Expose database session dependencies at the global package level, at the same level as the database configuration. Instructions- Create
app/dependencies.py. - Define the
get_dbgenerator function to handle database sessions.
Solution
Solution
CodeFile: Git ActionsCommit the changes:
app/dependencies.pyStep 7: Define Global Exceptions
Objective Design exception models to categorize API errors cleanly. Instructions- Create custom exceptions inside
app/core/exceptions.py. - Inherit exceptions from a base
AppExceptionclass.
Solution
Solution
CodeFile: Git ActionsCommit the changes:
app/core/exceptions.pyStep 8: Define Exception Handlers
Objective Convert custom application errors into structured HTTP responses. Instructions- Create
app/core/exception_handlers.py. - Write the exception handler helper to return JSONResponses.
Solution
Solution
CodeFile: Git ActionsCommit the changes:
app/core/exception_handlers.pyStep 9: Design the User Database Model
Objective Create the User database model with a role attribute to handle Admin vs. Author permissions. Instructions- Create
app/models/user.py. - Define fields mapping the
userstable. - Declare relationships to user posts.
- id: Integer, Primary Key, Indexed
- email: String(100), Unique, Indexed, Not Null
- hashed_password: String(200), Not Null
- name: String(100), Not Null
- role: String(20), Not Null (Defaults to
"author") - Relationship: Has a one-to-many relationship with
Postmapped viapostsback-populatingauthor(usingcascade="all, delete-orphan")
Solution
Solution
CodeFile: Git ActionsCommit the changes:
app/models/user.pyStep 10: Design the Post Database Model
Objective Define the blog Post model referencing the User model via a foreign key relationship. Instructions- Create
app/models/post.py. - Map column attributes for the
poststable. - Configure ForeignKey relations pointing user identities to author identifiers.
- id: Integer, Primary Key, Indexed
- title: String(200), Not Null
- content: Text, Not Null
- author_id: Integer, ForeignKey to
users.id(with cascade delete rules), Not Null - Relationship: Belongs to
Userback-populatingposts
Solution
Solution
CodeFile: Git ActionsCommit the changes:
app/models/post.pyStep 11: Create User Validation Schemas
Objective Create User data validation schemas using Pydantic inheritance and modern Annotated validators. Instructions- Create
app/schemas/user.py. - Use inheritance starting from
UserBase. - Add field validation constraints.
- UserBase:
email: Validated EmailStrname: string (length between 2 and 50)
- UserCreate: Inherits from
UserBaseand addspassword(minimum length of 6) androle(defaults to"author") - UserUpdate: Optional fields for
name,email, androle - UserResponse: Inherits from
UserBaseand addsidandrole(configured withfrom_attributes = True)
Solution
Solution
CodeFile: Git ActionsCommit the changes:
app/schemas/user.pyStep 12: Create Post Validation Schemas
Objective Implement data validation schemas for creating, updating, and returning blog posts. Instructions- Create
app/schemas/post.py. - Implement
PostBaseand its subclasses.
- PostBase:
title: string (length between 3 and 200)content: string (minimum length of 1)
- PostCreate: Inherits from
PostBase - PostUpdate: Optional fields for
titleandcontent - PostResponse: Inherits from
PostBaseand addsid,author_id, andauthor(configured withfrom_attributes = True)
Solution
Solution
CodeFile: Git ActionsCommit the changes:
app/schemas/post.pyStep 13: Create Token Validation Schemas
Objective Create Pydantic models for authentication JWT tokens. Instructions- Create
app/schemas/token.py. - Define models to validate login responses.
Solution
Solution
CodeFile: Git ActionsCommit the changes:
app/schemas/token.pyStep 14: Core Security and Password Utilities
Objective Provide helper utilities for secure password storage. Instructions- Create
app/core/security.py. - Define helper actions to hash credentials and verify passwords.
Solution
Solution
CodeFile: Git ActionsCommit the changes:
app/core/security.pyStep 15: Create the User Repository
Objective Separate direct database operations on the User records using a Repository class. Instructions- Create
app/repositories/user_repository.py. - Implement routines to retrieve, create, and update User rows.
Solution
Solution
CodeFile: Git ActionsCommit the changes:
app/repositories/user_repository.pyStep 16: Create the Post Repository
Objective Separate database operations on the Post records using a Repository class. Instructions- Create
app/repositories/post_repository.py. - Implement database queries to create, read, update, and delete Post entries.
Solution
Solution
CodeFile: Git ActionsCommit the changes:
app/repositories/post_repository.pyStep 17: Create the User Service
Objective Coordinate User validation and profile updates within the Service Layer. Instructions- Create
app/services/user_service.py. - Write user profile updates and permission checks.
Solution
Solution
CodeFile: Git ActionsCommit the changes:
app/services/user_service.pyStep 18: Create the Post Service
Objective Coordinate Post mutations and authorization limit audits within the Service Layer. Instructions- Create
app/services/post_service.py. - Write logic to restrict post updates and deletions to the post owner or users with the Admin role.
Solution
Solution
CodeFile: Git ActionsCommit the changes:
app/services/post_service.py403 Forbidden error when trying to delete a post.
Step 19: Implement Authentication Dependencies in Dependencies.py
Objective Expose current active user context validation helper in the global dependencies file. Instructions- Open
app/dependencies.py. - Append the authentication dependencies (
get_current_userand annotations) to the same level asget_db.
Solution
Solution
CodeFile: Git ActionsCommit dependency updates:
app/dependencies.pyapp/dependencies.py compiles cleanly containing both database session and OAuth2 authentication handlers.
Step 20: Create API Routers
Objective Expose operations through API Routers usingAnnotated injection syntax.
Instructions
- Create
app/routers/auth.py,app/routers/user.py, andapp/routers/post.py. - Import dependencies from
app.dependenciesto enforce authorization.
Solution
Solution
CodeAuth Router (User Router (Post Router (Git ActionsCommit routers:
app/routers/auth.py)app/routers/user.py)app/routers/post.py)Step 21: Setup Application Entrypoint (main.py)
Objective Integrate your routes, create database tables, register global exception handlers, and start your server. Instructions- Create
app/main.py. - Include the declarative base model definitions to create tables on startup.
- Register
AppExceptionhandler using your custom function handler. - Register the Auth, User, and Post routers.
Solution
Solution
CodeGit ActionsCommit the final main configuration:
- Start your server:
- Navigate to
http://127.0.0.1:8000/docsto test endpoints via Swagger UI. - Test endpoint access:
- Register an
adminuser and anauthoruser. - Test that authors can edit their own posts but get
403 Forbiddentrying to edit others. - Test that admins can modify or delete any post and view/manage the user accounts list.
- Register an