A production-ready Flask REST API featuring JWT authentication, comprehensive Student CRUD operations, and a clean Repository Pattern architecture designed for seamless extensibility from JSON file storage to any database backend.
Built with industry-standard patterns: Application Factory, Blueprints, Service Layer, Repository Abstraction, Docker, and GitHub Actions CI/CD.
- Features
- Project Structure
- Architecture
- Tech Stack
- Getting Started
- API Reference
- Data Models
- Testing
- Docker Details
- CI/CD Pipeline
- Security Features
- Extending to a Database
- Troubleshooting
- Contributing
- License
| Category | Features |
|---|---|
| Authentication | JWT-based auth with access & refresh tokens, secure password hashing (PBKDF2-SHA256), role-based user management |
| Student Management | Full CRUD operations, email uniqueness validation, enrollment tracking, active/inactive status |
| Architecture | Repository Pattern, Service Layer, Application Factory, Blueprint-based routing |
| API Design | RESTful endpoints, versioned API (/api/v1), consistent JSON responses, comprehensive error handling |
| DevOps | Docker multi-stage builds, Docker Compose support, GitHub Actions CI/CD, health checks |
| Testing | 12+ automated tests, pytest with coverage reports, isolated test fixtures |
| Security | Non-root Docker user, token expiration, input validation, error sanitization |
RestApiGithub/
β
βββ app/ # ββ Application Package ββ
β βββ __init__.py # Application factory (create_app)
β βββ config.py # Environment-based config (dev/test/prod)
β βββ extensions.py # Flask extension instances (JWT)
β βββ errors.py # Global error handlers (400, 401, 404, 500)
β β
β βββ api/ # ββ API Layer (Blueprints) ββ
β β βββ __init__.py
β β βββ health.py # GET /api/v1/health
β β βββ auth.py # POST /api/v1/auth/register & login
β β βββ students.py # CRUD /api/v1/students
β β
β βββ models/ # ββ Data Models ββ
β β βββ __init__.py
β β βββ user.py # User dataclass (id, username, password_hash, role)
β β βββ student.py # Student dataclass (id, name, email, course, etc.)
β β
β βββ repositories/ # ββ Data Access Layer ββ
β β βββ __init__.py
β β βββ base_repository.py # Abstract interface (swap JSON β DB here)
β β βββ json_repository.py # JSON file-backed implementation
β β
β βββ services/ # ββ Business Logic Layer ββ
β βββ __init__.py
β βββ auth_service.py # Register, login, JWT token generation
β βββ student_service.py # Student CRUD operations
β
βββ tests/ # ββ Test Suite (12 tests) ββ
β βββ conftest.py # Shared fixtures (app, client, auth_headers)
β βββ test_health.py # Health endpoint test
β βββ test_auth.py # Auth endpoint tests (5 tests)
β βββ test_students.py # Student endpoint tests (6 tests)
β
βββ data/ # ββ Runtime JSON Data Store (git-ignored) ββ
β βββ users.json # Created on first user registration
β βββ students.json # Created on first student creation
β
βββ .github/
β βββ workflows/
β βββ ci.yml # GitHub Actions: Lint β Test β Zip β Docker
β
βββ Dockerfile # Multi-stage production build (gunicorn)
βββ .dockerignore # Files excluded from Docker build
βββ docker-compose.yml # One-command Docker deployment
βββ requirements.txt # Python dependencies
βββ pytest.ini # Pytest configuration
βββ run.py # Development entry point
βββ wsgi.py # Production WSGI entry (gunicorn)
βββ test_api_manual.py # Manual API test script
βββ .env.example # Environment variable template
βββ .gitignore # Git ignore rules
βββ README.md # This file
The project follows the Repository Pattern with clear separation of concerns:
ββββββββββββββββββββ ββββββββββββββββββββ ββββββββββββββββββββ ββββββββββββββββββββ
β API Layer β β Service Layer β β Repository Layer β β Data Store β
β (Blueprints) β βββΆ β (Business Logic) β βββΆ β (Interface) β βββΆ β (JSON / DB) β
β β β β β β β β
β health.py β β auth_service.py β β base_repository β β users.json β
β auth.py β β student_service β β json_repository β β students.json β
β students.py β β β β β β β
ββββββββββββββββββββ ββββββββββββββββββββ ββββββββββββββββββββ ββββββββββββββββββββ
| Layer | Responsibility | Benefit |
|---|---|---|
| API Layer | HTTP request/response handling, input validation | Thin controllers, easy to test |
| Service Layer | Business logic, data transformation | Reusable across different interfaces |
| Repository Layer | Data access abstraction | Swap data stores without code changes |
| Data Store | Persistence (JSON/DB) | Flexible storage options |
Client Request
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Flask Application β
β βββββββββββββββ βββββββββββββββ βββββββββββββββ β
β β JWT β β Error β β Config β β
β β Auth β β Handlers β β Manager β β
β βββββββββββββββ βββββββββββββββ βββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Blueprint (API Layer) β
β β’ Route definitions β
β β’ Request parsing β
β β’ Response formatting β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Service Layer β
β β’ Business logic β
β β’ Validation rules β
β β’ Token generation β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Repository Layer β
β β’ CRUD operations β
β β’ Data serialization β
β β’ Thread-safe operations β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
Data Store
(JSON/Database)
| Category | Technology | Version | Purpose |
|---|---|---|---|
| Runtime | Python | 3.10+ | Programming language |
| Framework | Flask | 3.1.x | Web framework |
| Authentication | Flask-JWT-Extended | 4.7.x | JWT token handling |
| Security | Werkzeug | 3.1.x | Password hashing |
| Production Server | Gunicorn | 23.0.x | WSGI HTTP server |
| Testing | pytest | 8.3.x | Test framework |
| Coverage | pytest-cov | 6.0.x | Code coverage |
| Linting | flake8 | 7.1.x | Code quality |
| Containerization | Docker | Latest | Container runtime |
| Orchestration | Docker Compose | 3.9 | Multi-container apps |
| Requirement | Version | Required |
|---|---|---|
| Python | 3.10 or higher | β Yes |
| pip | Latest | β Yes |
| Docker | Latest | β‘ Optional |
| Docker Compose | v2+ | β‘ Optional |
| Git | Latest | β‘ Optional |
# Clone the repository
git clone https://github.com/<your-username>/RestApiGithub.git
cd RestApiGithub
# Create virtual environment
python -m venv venv
# Activate virtual environment
# Windows (PowerShell)
.\venv\Scripts\Activate.ps1
# Windows (CMD)
venv\Scripts\activate.bat
# macOS / Linux
source venv/bin/activate
# Install dependencies
pip install -r requirements.txt
# Start the development server
python run.pyThe API is now running at http://localhost:5000
# Check health endpoint
curl http://localhost:5000/api/v1/health
# Expected response:
# {"status":"healthy","timestamp":"2026-02-20T..."}# Build and start in foreground
docker-compose up --build
# Build and start in background
docker-compose up --build -d
# View logs
docker-compose logs -f
# Stop and remove containers
docker-compose down
# Stop and remove containers + volumes
docker-compose down -v# Build the image
docker build -t student-api .
# Run the container
docker run -p 5000:5000 \
-e SECRET_KEY=my-secret-key \
-e JWT_SECRET_KEY=jwt-secret-key \
-v student-data:/app/data \
--name student-api \
student-api
# Check logs
docker logs -f student-api
# Stop the container
docker stop student-api
# Remove the container
docker rm student-apiCreate a .env file in the project root (copy from .env.example):
cp .env.example .env| Variable | Default | Description | Required |
|---|---|---|---|
FLASK_ENV |
development |
Environment mode: development, testing, production |
No |
SECRET_KEY |
change-me-in-production |
Flask secret key for session signing | Yes (prod) |
JWT_SECRET_KEY |
jwt-change-me-in-production |
Secret key for JWT token signing | Yes (prod) |
JWT_ACCESS_TOKEN_HOURS |
1 |
Access token validity period in hours | No |
JWT_REFRESH_TOKEN_DAYS |
30 |
Refresh token validity period in days | No |
PORT |
5000 |
Server listening port | No |
DATA_DIR |
./data |
Directory for JSON data storage | No |
β οΈ Security Warning: Always use strong, unique values forSECRET_KEYandJWT_SECRET_KEYin production!
Base URL: http://localhost:5000/api/v1
Content-Type: application/json
Check if the API is running and healthy.
| Method | Endpoint | Auth Required | Rate Limited |
|---|---|---|---|
| GET | /health |
β No | β No |
GET /api/v1/health{
"status": "healthy",
"timestamp": "2026-02-20T10:30:00+00:00"
}Create a new user account.
| Method | Endpoint | Auth Required |
|---|---|---|
| POST | /auth/register |
β No |
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
username |
string | β Yes | Unique username (min 1 char) |
password |
string | β Yes | User password (min 1 char) |
role |
string | β No | User role (default: "user") |
POST /api/v1/auth/register
Content-Type: application/json
{
"username": "admin",
"password": "Admin123!",
"role": "admin"
}Success Response (201 Created):
{
"message": "User registered successfully",
"user": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"username": "admin",
"role": "admin"
}
}Error Responses:
| Status | Condition | Response |
|---|---|---|
| 400 | Missing username or password | {"error": "username and password are required"} |
| 409 | Username already exists | {"error": "Username already exists"} |
Authenticate and receive JWT tokens.
| Method | Endpoint | Auth Required |
|---|---|---|
| POST | /auth/login |
β No |
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
username |
string | β Yes | Registered username |
password |
string | β Yes | User password |
POST /api/v1/auth/login
Content-Type: application/json
{
"username": "admin",
"password": "Admin123!"
}Success Response (200 OK):
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"username": "admin",
"role": "admin"
}
}Error Responses:
| Status | Condition | Response |
|---|---|---|
| 400 | Missing credentials | {"error": "username and password are required"} |
| 401 | Invalid credentials | {"error": "Invalid username or password"} |
π Important: Copy the
access_tokenfrom the login response. Use it in theAuthorizationheader for all protected endpoints.
All student endpoints require authentication via Bearer token:
Authorization: Bearer <access_token>| Method | Endpoint | Auth Required |
|---|---|---|
| GET | /students |
β Yes |
GET /api/v1/students
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...Response (200 OK):
{
"count": 2,
"students": [
{
"id": "f5e6d7c8-...",
"first_name": "Alice",
"last_name": "Smith",
"email": "alice@university.com",
"course": "Computer Science",
"enrollment_date": "2026-02-13T10:30:00+00:00",
"is_active": true
},
{
"id": "a1b2c3d4-...",
"first_name": "Bob",
"last_name": "Johnson",
"email": "bob@university.com",
"course": "Mathematics",
"enrollment_date": "2026-02-14T09:15:00+00:00",
"is_active": true
}
]
}| Method | Endpoint | Auth Required |
|---|---|---|
| GET | /students/<id> |
β Yes |
GET /api/v1/students/f5e6d7c8-1234-5678-abcd-ef1234567890
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...Response (200 OK):
{
"id": "f5e6d7c8-1234-5678-abcd-ef1234567890",
"first_name": "Alice",
"last_name": "Smith",
"email": "alice@university.com",
"course": "Computer Science",
"enrollment_date": "2026-02-13T10:30:00+00:00",
"is_active": true
}Error Response (404 Not Found):
{
"error": "Student not found"
}| Method | Endpoint | Auth Required |
|---|---|---|
| POST | /students |
β Yes |
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
first_name |
string | β Yes | Student's first name |
last_name |
string | β Yes | Student's last name |
email |
string | β Yes | Unique email address |
course |
string | β Yes | Enrolled course name |
POST /api/v1/students
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Content-Type: application/json
{
"first_name": "Alice",
"last_name": "Smith",
"email": "alice@university.com",
"course": "Computer Science"
}Response (201 Created):
{
"message": "Student created",
"student": {
"id": "f5e6d7c8-1234-5678-abcd-ef1234567890",
"first_name": "Alice",
"last_name": "Smith",
"email": "alice@university.com",
"course": "Computer Science",
"enrollment_date": "2026-02-20T10:30:00+00:00",
"is_active": true
}
}Error Responses:
| Status | Condition | Response |
|---|---|---|
| 400 | Missing required fields | {"error": "Missing fields: first_name, email"} |
| 409 | Email already exists | {"error": "A student with this email already exists"} |
| Method | Endpoint | Auth Required |
|---|---|---|
| PUT | /students/<id> |
β Yes |
Request Body (all fields optional):
| Field | Type | Description |
|---|---|---|
first_name |
string | Updated first name |
last_name |
string | Updated last name |
email |
string | Updated email |
course |
string | Updated course |
is_active |
boolean | Active status |
PUT /api/v1/students/f5e6d7c8-1234-5678-abcd-ef1234567890
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Content-Type: application/json
{
"course": "Data Science",
"is_active": false
}Response (200 OK):
{
"message": "Student updated",
"student": {
"id": "f5e6d7c8-1234-5678-abcd-ef1234567890",
"first_name": "Alice",
"last_name": "Smith",
"email": "alice@university.com",
"course": "Data Science",
"enrollment_date": "2026-02-13T10:30:00+00:00",
"is_active": false
}
}| Method | Endpoint | Auth Required |
|---|---|---|
| DELETE | /students/<id> |
β Yes |
DELETE /api/v1/students/f5e6d7c8-1234-5678-abcd-ef1234567890
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...Response (200 OK):
{
"message": "Student deleted"
}All API errors follow a consistent format:
{
"error": "Error type",
"message": "Detailed error description"
}| Status Code | Error Type | Common Causes |
|---|---|---|
| 400 | Bad Request | Missing required fields, invalid JSON |
| 401 | Unauthorized | Missing/invalid/expired JWT token |
| 403 | Forbidden | Insufficient permissions |
| 404 | Not Found | Resource doesn't exist |
| 409 | Conflict | Duplicate username or email |
| 422 | Unprocessable Entity | Validation failed |
| 500 | Internal Server Error | Server-side error (details logged) |
@dataclass
class User:
id: str # UUID v4 (auto-generated)
username: str # Unique username
password_hash: str # PBKDF2-SHA256 hashed password
role: str = "user" # User role (default: "user")JSON Storage Format:
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"username": "admin",
"password_hash": "pbkdf2:sha256:600000$...",
"role": "admin"
}@dataclass
class Student:
id: str # UUID v4 (auto-generated)
first_name: str # Student's first name
last_name: str # Student's last name
email: str # Unique email address
course: str # Enrolled course
enrollment_date: str # ISO 8601 timestamp (auto-generated)
is_active: bool = True # Active status (default: True)JSON Storage Format:
{
"id": "f5e6d7c8-1234-5678-abcd-ef1234567890",
"first_name": "Alice",
"last_name": "Smith",
"email": "alice@university.com",
"course": "Computer Science",
"enrollment_date": "2026-02-20T10:30:00+00:00",
"is_active": true
}The project includes 12+ automated tests covering all endpoints:
# Run all tests
pytest
# Run with verbose output
pytest -v
# Run specific test file
pytest tests/test_students.py -v
# Run with coverage report
pytest --cov=app --cov-report=term-missing
# Generate HTML coverage report
pytest --cov=app --cov-report=html
# Open htmlcov/index.html in browserTest Coverage Breakdown:
| Test File | Tests | Description |
|---|---|---|
test_health.py |
1 | Health endpoint verification |
test_auth.py |
5 | Registration, duplicate handling, login, authentication |
test_students.py |
6 | CRUD operations, authorization checks |
Test Fixtures:
# tests/conftest.py
@pytest.fixture
def app():
"""Create test application instance."""
@pytest.fixture
def client(app):
"""Create test client."""
@pytest.fixture
def auth_headers(client):
"""Get authentication headers for protected endpoints."""-
Register a new user
POST http://localhost:5000/api/v1/auth/register- Body:
{"username": "testuser", "password": "Test123!"}
-
Login to get tokens
POST http://localhost:5000/api/v1/auth/login- Body:
{"username": "testuser", "password": "Test123!"} - Copy the
access_tokenfrom response
-
Configure Authorization
- Go to Authorization tab
- Select Bearer Token
- Paste the access token
-
Test Student Endpoints
- Now you can access all
/studentsendpoints
- Now you can access all
Create a Postman collection with these requests:
π Student API
βββ π’ Health Check
β βββ GET {{base_url}}/health
βββ π Auth
β βββ POST {{base_url}}/auth/register
β βββ POST {{base_url}}/auth/login
βββ π Students
βββ GET {{base_url}}/students
βββ GET {{base_url}}/students/:id
βββ POST {{base_url}}/students
βββ PUT {{base_url}}/students/:id
βββ DELETE {{base_url}}/students/:id
# 1. Register a new user
curl -X POST http://localhost:5000/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{"username": "admin", "password": "Admin123!"}'
# 2. Login and save token
TOKEN=$(curl -s -X POST http://localhost:5000/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "admin", "password": "Admin123!"}' | jq -r '.access_token')
# 3. Create a student
curl -X POST http://localhost:5000/api/v1/students \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"first_name": "Alice", "last_name": "Smith", "email": "alice@example.com", "course": "CS"}'
# 4. List all students
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:5000/api/v1/students
# 5. Get a specific student (replace STUDENT_ID)
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:5000/api/v1/students/<STUDENT_ID>
# 6. Update a student
curl -X PUT http://localhost:5000/api/v1/students/<STUDENT_ID> \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"course": "Data Science"}'
# 7. Delete a student
curl -X DELETE -H "Authorization: Bearer $TOKEN" \
http://localhost:5000/api/v1/students/<STUDENT_ID># Register
$response = Invoke-RestMethod -Uri "http://localhost:5000/api/v1/auth/register" `
-Method POST -ContentType "application/json" `
-Body '{"username": "admin", "password": "Admin123!"}'
# Login
$login = Invoke-RestMethod -Uri "http://localhost:5000/api/v1/auth/login" `
-Method POST -ContentType "application/json" `
-Body '{"username": "admin", "password": "Admin123!"}'
$token = $login.access_token
# Create Student
Invoke-RestMethod -Uri "http://localhost:5000/api/v1/students" `
-Method POST -ContentType "application/json" `
-Headers @{Authorization = "Bearer $token"} `
-Body '{"first_name": "Alice", "last_name": "Smith", "email": "alice@example.com", "course": "CS"}'
# List Students
Invoke-RestMethod -Uri "http://localhost:5000/api/v1/students" `
-Headers @{Authorization = "Bearer $token"}| Feature | Description |
|---|---|
| Multi-stage build | Smaller final image (~150MB) |
| Non-root user | Runs as appuser for security |
| Gunicorn | Production WSGI server with 4 workers |
| Health check | Built-in HEALTHCHECK instruction |
| Slim base | python:3.12-slim for minimal footprint |
# Stage 1: Build dependencies
FROM python:3.12-slim AS builder
# Install Python packages to /install prefix
# Stage 2: Production image
FROM python:3.12-slim AS production
# Create non-root user
# Copy only necessary files
# Configure health check
# Run with Gunicorn# Build image
docker build -t student-api .
# Build with no cache
docker build --no-cache -t student-api .
# Run container (foreground)
docker run -p 5000:5000 \
-e SECRET_KEY=my-secret \
-e JWT_SECRET_KEY=jwt-secret \
student-api
# Run container (background)
docker run -d -p 5000:5000 \
-e SECRET_KEY=my-secret \
-e JWT_SECRET_KEY=jwt-secret \
--name student-api \
student-api
# View running containers
docker ps
# View logs
docker logs student-api
docker logs -f student-api # Follow logs
# Execute command in container
docker exec -it student-api /bin/bash
# Stop container
docker stop student-api
# Remove container
docker rm student-api
# Remove image
docker rmi student-api# Start services
docker-compose up
# Start in background
docker-compose up -d
# Rebuild and start
docker-compose up --build
# View logs
docker-compose logs
docker-compose logs -f api # Follow specific service
# Stop services
docker-compose stop
# Stop and remove
docker-compose down
# Stop, remove, and delete volumes
docker-compose down -v
# View running services
docker-compose psThe workflow at .github/workflows/ci.yml runs on every push to main/develop and on pull requests:
βββββββββββ βββββββββββ βββββββββββββββ ββββββββββββββββ
β Lint β βββΆ β Test β βββΆ β Build & Zip β βββΆ β Docker Build β
β (flake8)β β (pytest)β β (artifact) β β (verify) β
βββββββββββ βββββββββββ βββββββββββββββ ββββββββββββββββ
β β β
βΌ βΌ βΌ
π Coverage π¦ student-api.zip π³ Docker image
Report (artifact download) (health check)
| Stage | Tool | Purpose | Failure Action |
|---|---|---|---|
| Lint | flake8 | Static code analysis | β Block merge |
| Test | pytest | Run automated tests | β Block merge |
| Coverage | pytest-cov | Generate coverage report | π Upload artifact |
| Build | zip | Create deployable package | π¦ Upload artifact |
| Docker | docker build | Verify container builds | β Block merge |
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]| Feature | Implementation | Details |
|---|---|---|
| Password Hashing | PBKDF2-SHA256 | Werkzeug's generate_password_hash() |
| JWT Tokens | HS256 signing | Short-lived access (1h), long-lived refresh (30d) |
| Protected Endpoints | @jwt_required() |
All student APIs require valid token |
| Non-root Docker | appuser |
Container runs as unprivileged user |
| Error Sanitization | Custom handlers | 500 errors never leak internal details |
| Input Validation | Service layer | All inputs validated before processing |
| Thread Safety | Locking | JSON repository uses threading locks |
-
Always change default secrets in production
export SECRET_KEY=$(openssl rand -hex 32) export JWT_SECRET_KEY=$(openssl rand -hex 32)
-
Use HTTPS in production (configure reverse proxy)
-
Implement rate limiting for auth endpoints (future enhancement)
-
Regular dependency updates
pip install --upgrade -r requirements.txt
The project is designed to swap from JSON files to any database with zero changes to API or service code:
# app/repositories/sqlalchemy_repository.py
from typing import Optional, TypeVar, Type
from sqlalchemy.orm import Session
from app.repositories.base_repository import BaseRepository
T = TypeVar("T")
class SQLAlchemyRepository(BaseRepository[T]):
"""SQLAlchemy-backed repository implementation."""
def __init__(self, session: Session, model_cls: Type[T]) -> None:
self._session = session
self._model_cls = model_cls
def get_all(self) -> list[T]:
return self._session.query(self._model_cls).all()
def get_by_id(self, entity_id: str) -> Optional[T]:
return self._session.query(self._model_cls).get(entity_id)
def create(self, entity: T) -> T:
self._session.add(entity)
self._session.commit()
return entity
def update(self, entity_id: str, entity: T) -> Optional[T]:
existing = self.get_by_id(entity_id)
if existing:
# Update fields
self._session.commit()
return existing
def delete(self, entity_id: str) -> bool:
entity = self.get_by_id(entity_id)
if entity:
self._session.delete(entity)
self._session.commit()
return True
return False# app/api/students.py
def _get_service() -> StudentService:
# Option 1: JSON (current)
# repo = JsonRepository[Student](filepath, Student)
# Option 2: SQLAlchemy
# repo = SQLAlchemyRepository[Student](db.session, StudentModel)
return StudentService(repo)| Database | Package | Use Case |
|---|---|---|
| PostgreSQL | psycopg2-binary, SQLAlchemy |
Production, relational data |
| MySQL | pymysql, SQLAlchemy |
Production, relational data |
| MongoDB | pymongo |
Document storage, flexible schema |
| SQLite | Built-in | Development, small deployments |
| Redis | redis |
Caching, session storage |
Error: Address already in use (port 5000)
Solution:
# Find process using port
netstat -ano | findstr :5000 # Windows
lsof -i :5000 # macOS/Linux
# Kill process or use different port
python run.py # Uses PORT env variable{"error": "Token has expired"}Solution: Login again to get a new access token.
{"error": "Missing Authorization Header"}Solution: Include the Bearer token:
curl -H "Authorization: Bearer <your_token>" ...Error: pip install failed
Solution:
# Clear Docker cache
docker builder prune
docker build --no-cache -t student-api .ModuleNotFoundError: No module named 'app'
Solution:
# Ensure you're in the project root
cd RestApiGithub
# Install in development mode
pip install -e .
# Or run from project root
python -m pytestEnable detailed logging:
export FLASK_ENV=development
export FLASK_DEBUG=1
python run.pyWe welcome contributions! Please follow these steps:
git clone https://github.com/<your-username>/RestApiGithub.git
cd RestApiGithubgit checkout -b feature/your-feature-name- Follow existing code style
- Add tests for new features
- Update documentation
# Lint
flake8 app tests
# Test
pytest -v
# Coverage
pytest --cov=app --cov-report=term-missinggit add .
git commit -m "feat: add your feature description"
git push origin feature/your-feature-name- Open PR against
developbranch - Describe your changes
- Wait for CI checks to pass
- Request review
<type>(<scope>): <description>
Types:
- feat: New feature
- fix: Bug fix
- docs: Documentation
- style: Formatting
- refactor: Code restructuring
- test: Adding tests
- chore: Maintenance
Data is stored as JSON files in the data/ directory (auto-created at runtime):
| File | Contents | Notes |
|---|---|---|
users.json |
Registered user accounts | Passwords are hashed |
students.json |
Student records | Auto-generated enrollment date |
The data/ folder is:
- β Git-ignored (not committed to repository)
- β Persisted via Docker volume in containerized deployments
- β Thread-safe with file locking
This project is licensed under the MIT License.
MIT License
Copyright (c) 2026
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
- Issues: GitHub Issues
- Discussions: GitHub Discussions
Made with β€οΈ using Flask