SchoolHub — Full-Stack SDE Master Course
One complete project. One learning path. Build a production-style School Management System from zero, while learning the Python, FastAPI, SQL/PostgreSQL, React and engineering fundamentals needed for junior SDE interviews.
This is deliberately practical: every major concept ends in code, and the project grows chapter by chapter instead of being dumped on you at the end.
00. How To Use This Course
The project grows like a real product
Phase 1 → Python foundation Phase 2 → HTTP / REST understanding Phase 3 → FastAPI API Phase 4 → PostgreSQL database Phase 5 → Authentication + roles Phase 6 → School management backend Phase 7 → React frontend Phase 8 → Connect React + API Phase 9 → Testing + security Phase 10 → Docker + deployment Phase 11 → System design + interview defense
â€Å“NOW I WILL DO THIS†format
1. Create the folder.
2. Install the dependencies.
3. Write the exact file(s).
4. Run it.
5. Test the happy path.
6. Test one failure case.
7. Explain why it works.
01. Master Roadmap
| Phase | Learn | Project milestone |
|---|---|---|
| 1 | Python syntax, functions, collections, OOP, exceptions, modules, typing, async basics | Python mini utilities |
| 2 | Git, virtual environments, packages, debugging | Clean repository |
| 3 | HTTP, JSON, REST, status codes, browser/client/server | API design plan |
| 4 | FastAPI routing, dependencies, schemas, responses, errors | First SchoolHub APIs |
| 5 | SQL, PostgreSQL, constraints, joins, indexes, transactions | School database |
| 6 | SQLAlchemy 2.x, sessions, relationships, Alembic | Persistent backend |
| 7 | Password hashing, JWT, OAuth2 concepts, RBAC | Login + Admin/Teacher/Student access |
| 8 | React, JSX, components, props, state, hooks, routing, forms | Dashboard UI |
| 9 | API integration, loading/error states, auth state | Full-stack application |
| 10 | Testing, logging, CORS, security, Docker | Production-style project |
| 11 | Architecture, performance, scalability, interview questions | SDE-ready explanation |
02. Python Foundations — Beginner to Backend-Ready
FastAPI becomes easy when Python fundamentals are solid. Learn these in order.
2.1 Variables and types
name = "Avi" # str
age = 22 # int
percentage = 82.5 # float
active = True # bool
marks = [80, 72, 91] # list
student = { # dict
"name": "Avi",
"age": 22
}
Python is dynamically typed, but type hints make backend code easier to understand, validate and maintain.
name: str = "Avi"
age: int = 22
marks: list[int] = [80, 72, 91]
2.2 Conditions
marks = 82
if marks >= 90:
grade = "A+"
elif marks >= 75:
grade = "A"
else:
grade = "B"
print(grade)
2.3 Loops
students = ["Avi", "Riya", "Rahul"]
for student in students:
print(student)
count = 3
while count > 0:
print(count)
count -= 1
2.4 Functions — core backend skill
def calculate_average(marks: list[int]) -> float:
if not marks:
return 0.0
return sum(marks) / len(marks)
average = calculate_average([80, 90, 70])
print(average)
Why important? API endpoints, services, repositories and utilities are all functions/classes working together.
2.5 Arguments
def create_student(name: str, age: int = 18):
return {"name": name, "age": age}
create_student("Avi")
create_student("Avi", 22)
2.6 List / dict comprehensions
marks = [40, 75, 90, 32, 81]
passed = [m for m in marks if m >= 40]
students = [
{"name": "Avi", "marks": 90},
{"name": "Riya", "marks": 80}
]
names = [s["name"] for s in students]
2.7 Mutable vs immutable
list and dict are mutable. Strings, integers and tuples are immutable. This matters when objects are shared between functions.
2.8 *args and **kwargs
def total(*numbers: int) -> int:
return sum(numbers)
def show_student(**data):
print(data)
total(10, 20, 30)
show_student(name="Avi", age=22)
2.9 Exceptions
try:
age = int("abc")
except ValueError:
print("Invalid age")
finally:
print("Finished")
Custom exception
class InsufficientBalanceError(Exception):
pass
def withdraw(balance: float, amount: float):
if amount > balance:
raise InsufficientBalanceError("Insufficient balance")
return balance - amount
2.10 Modules and imports
# math_utils.py
def add(a: int, b: int) -> int:
return a + b
# main.py
from math_utils import add
print(add(2, 3))
2.11 OOP
class Student:
def __init__(self, name: str, roll_no: int):
self.name = name
self.roll_no = roll_no
def introduce(self) -> str:
return f"{self.name} - Roll {self.roll_no}"
student = Student("Avi", 101)
print(student.introduce())
Encapsulation idea
Keep an object's internal state controlled through methods/properties rather than allowing every part of the application to mutate it freely.
Inheritance vs composition
class Person:
def speak(self):
return "Hello"
class Teacher(Person):
pass
Inheritance expresses an â€Å“is-a†relationship. Composition expresses a â€Å“has-a†relationship and is often easier to change as systems grow.
Dataclasses
from dataclasses import dataclass
@dataclass
class Student:
name: str
roll_no: int
2.12 Type hints
from typing import Optional
def find_student(student_id: int) -> Optional[Student]:
...
Modern Python also supports union syntax:
def find_student(student_id: int) -> Student | None:
...
2.13 Lambda / map / filter — know them, don't overuse them
numbers = [1, 2, 3, 4]
squares = list(map(lambda x: x * x, numbers))
even = list(filter(lambda x: x % 2 == 0, numbers))
2.14 Iterators and generators
def generate_numbers():
for i in range(3):
yield i
for number in generate_numbers():
print(number)
Generators produce values lazily and can reduce memory usage for large sequences.
2.15 Decorators — understand the concept
def logger(func):
def wrapper(*args, **kwargs):
print("Calling function")
result = func(*args, **kwargs)
print("Done")
return result
return wrapper
@logger
def add(a, b):
return a + b
Frameworks use decorators heavily: @app.get(), @app.post() etc.
2.16 Context managers
with open("notes.txt", "r") as file:
content = file.read()
They provide reliable setup/cleanup. Database sessions and files are common examples.
2.17 Async/await
import asyncio
async def fetch_student():
await asyncio.sleep(1)
return {"name": "Avi"}
async def main():
student = await fetch_student()
print(student)
asyncio.run(main())
async does not magically make blocking code asynchronous. Async shines for I/O-bound work when the libraries you call support asynchronous operation.2.18 Python backend checklist
- Functions + scope
- List/dict/set/tuple
- Exceptions
- OOP
- Modules/packages
- Type hints
- Decorators
- Generators
- Context managers
- async/await
- Virtual environments + packages
03. Git + Environment
Recommended structure
schoolhub/
â”ω”€Ã¢”€ backend/
â”ω”€Ã¢”€ frontend/
â”ω”€Ã¢”€ README.md
└── .gitignore
Git essentials
git init
git status
git add .
git commit -m "initial project setup"
git branch
git checkout -b feature/auth
git log --oneline
.gitignore
.venv/
__pycache__/
.env
*.pyc
node_modules/
dist/
04. HTTP + Web Fundamentals
Client → server
React Browser
|
| POST /auth/login + JSON
v
FastAPI
|
| SQL query
v
PostgreSQL
Request anatomy
POST /api/v1/auth/login HTTP/1.1
Host: api.schoolhub.com
Content-Type: application/json
Authorization: Bearer <token>
{
"email": "avi@example.com",
"password": "secret"
}
Response
HTTP/1.1 200 OK
Content-Type: application/json
{
"access_token": "...",
"token_type": "bearer"
}
Status codes
| Code | Meaning | SchoolHub example |
|---|---|---|
| 200 | Success | Fetch student |
| 201 | Created | Create student |
| 204 | No content | Successful delete |
| 400 | Bad request | Invalid business input |
| 401 | Unauthenticated | Missing/invalid login credentials |
| 403 | Forbidden | Student tries admin action |
| 404 | Not found | Student ID doesn't exist |
| 409 | Conflict | Duplicate email |
| 422 | Validation failure | Wrong request field type/constraints |
| 500 | Server failure | Unexpected backend error |
REST design
GET /students
GET /students/{id}
POST /students
PATCH /students/{id}
DELETE /students/{id}
GET /teachers
GET /classes
GET /attendance
05. FastAPI Foundations
Install
python -m venv .venv
# Windows
.venv\Scripts\activate
# macOS/Linux
source .venv/bin/activate
pip install "fastapi[standard]"
First app — backend/app/main.py
from fastapi import FastAPI
app = FastAPI(
title="SchoolHub API",
version="1.0.0"
)
@app.get("/")
def home():
return {"message": "SchoolHub API running"}
@app.get("/health")
def health():
return {"status": "ok"}
Run
fastapi dev app/main.py
FastAPI exposes interactive documentation at /docs, alternative documentation at /redoc, and the OpenAPI schema at /openapi.json.
Path parameters
@app.get("/students/{student_id}")
def get_student(student_id: int):
return {"student_id": student_id}
Query parameters
@app.get("/students")
def list_students(
page: int = 1,
limit: int = 10,
search: str | None = None
):
return {
"page": page,
"limit": limit,
"search": search
}
Request body
from pydantic import BaseModel
class StudentCreate(BaseModel):
name: str
roll_no: int
class_name: str
@app.post("/students")
def create_student(student: StudentCreate):
return student
Router separation
from fastapi import APIRouter
router = APIRouter(prefix="/students", tags=["Students"])
@router.get("/")
def list_students():
return []
# main.py
from app.api.students import router as student_router
app.include_router(student_router, prefix="/api/v1")
/api/v1/students, /api/v1/teachers, and /api/v1/classes routers and make their GET endpoints return temporary data.06. Pydantic + API Contracts
Separate create/read/update schemas
from pydantic import BaseModel, EmailStr, Field
class StudentCreate(BaseModel):
name: str = Field(min_length=2, max_length=100)
email: EmailStr
roll_no: int = Field(gt=0)
class_name: str
class StudentUpdate(BaseModel):
name: str | None = Field(default=None, min_length=2)
class_name: str | None = None
class StudentResponse(BaseModel):
id: int
name: str
email: EmailStr
roll_no: int
class_name: str
model_config = {"from_attributes": True}
Why separate models? A create request may require fields that an update doesn't. A response should expose only public fields, not password hashes or internal database metadata.
Validation example
class AttendanceCreate(BaseModel):
student_id: int = Field(gt=0)
present: bool
Serialization
Serialization converts Python/model data into a transport format such as JSON. Deserialization/validation turns incoming JSON into validated application data.