Self-hosted social publishing with a versioned API, PostgreSQL, and a React client.
Build a blog with posts, discussions, media, privacy controls, and staff moderation.
Quick start · Features · API flow · API docs · Architecture · Contributing
Simple Blog is a modular publishing application for teams that want to control
their content model and API contract. FastAPI serves the backend,
PostgreSQL stores relational state, MinIO provides local S3-compatible storage,
and the React client consumes /api/v1.
- Social publishing primitives: feeds, trending posts, categories, drafts, full-text search, comments, likes, bookmarks, and shares.
- Account and privacy controls: email verification, password reset, refresh sessions, profile/post/comment visibility, and HttpOnly cookies.
- Staff moderation: reports with target snapshots, category requests, user bans and mutes, role management, content hide/restore, and audit actions.
- Operational safeguards: CSRF protection, security headers, request IDs, structured errors, gzip responses, and rate limiting.
- Provider-neutral media: MIME and size validation with MinIO locally and S3-compatible storage.
Requirements: Docker Desktop and Docker Compose v2.
git clone https://github.com/Kene33/simple-blog.git
cd simple-blog
docker compose up --buildCompose starts FastAPI, PostgreSQL 16, MinIO, and the Alembic migration job.
Development connection values live in docker-compose.yml.
Check the service:
curl http://localhost:8000/health/live
curl http://localhost:8000/health/readyOpen the API contract in Swagger UI.
| Service | URL |
|---|---|
| API | http://localhost:8000 |
| Swagger UI | http://localhost:8000/docs |
| MinIO API | http://localhost:9000 |
| MinIO Console | http://localhost:9001 |
Open a second terminal:
cd src/frontend
npm ci
npm run devThe Vite client runs at http://localhost:5173 and proxies /api to the local
backend. Build the client with:
npm --prefix src/frontend run buildThe API uses /api/v1, JSON snake_case, UUID identifiers, UTC timestamps, and
items plus next_cursor for collection responses.
flowchart LR
Register[Register] --> Verify[Verify email]
Verify --> Login[Login]
Login --> Publish[Create post or draft]
Publish --> Discuss[Comment, like, bookmark, share]
Discuss --> Report[Report content]
Report --> Moderate[Staff resolves report]
Create an account and store its cookies:
curl -i -c cookies.txt \
-H 'Content-Type: application/json' \
-d '{"username":"reader_01","email":"reader@example.com","password":"change-me-123"}' \
http://localhost:8000/api/v1/auth/registerState-changing browser requests use the X-CSRF-Token header. The complete
request/response contract is documented in REST API v1 and
API schemas.
| Area | Current capabilities |
|---|---|
| Auth | Register, email verification, login, refresh, logout, password reset |
| Profiles | Public/private visibility, profile fields, author comments |
| Content | Posts, drafts, categories, category requests, trending, search, pagination |
| Media | Image/video uploads, cover media, ownership checks, S3/MinIO storage |
| Discussion | Nested comments, edits, soft-delete tombstones, visibility controls |
| Interactions | Idempotent likes, bookmarks, copy/native share events |
| Moderation | Reports, staff queue, bans, mutes, roles, hide/restore, audit log |
| Client | React 19, Vite, JSX, fetch API layer, responsive CSS |
The app adds security headers, request IDs, structured logs, gzip compression,
rate limiting, and one error envelope for API failures. Media quotas and upload
limits are configurable in src/core/config.py.
flowchart LR
Browser[React + Vite client] -->|/api/v1| App[FastAPI application]
App --> Auth[Auth and users]
App --> Content[Posts, drafts, categories]
App --> Social[Comments and interactions]
App --> Moderation[Reports and staff actions]
App --> DB[(PostgreSQL)]
App --> Storage[S3 / MinIO]
App --> Redis[(Rate limiter)]
Routers handle HTTP transport. Domain services enforce ownership, roles, and business rules. PostgreSQL owns relational state; object storage owns media; The rate limiter protects sensitive endpoints. The detailed boundaries live in architecture.md and backend-module-boundaries.md.
More documentation:
Backend CI uses Python 3.12, PostgreSQL 16, the locked Python dependencies,
Alembic, Ruff, pip-audit, and pytest.
ruff check src tests
pytest -q tests
alembic upgrade head --sql
pip-audit -r requirements.lock
npm --prefix src/frontend run check:post-files
npm --prefix src/frontend run check:comment-tree
npm --prefix src/frontend run buildsrc/
api/ versioned FastAPI routers
core/ config, security, rate limits, logging, errors
db/ async sessions, models, migrations glue
modules/ auth, users, posts, categories, media, comments,
interactions, moderation
frontend/ React/Vite client
docs/ API contracts and architecture notes
alembic/ PostgreSQL migrations
tests/ API, security, and PostgreSQL integration tests
Open an issue for a bug or idea. Before a pull request:
- Describe the problem and expected result.
- Update API docs with contract changes.
- Check ownership, CSRF, roles, visibility, and migrations.
- Run the relevant backend and frontend checks.
- List known limitations in the pull request.
Read the full rules in CONTRIBUTING.md.
Simple Blog is released under the MIT License.