Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

19 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

fastapi-blog

A full-stack blog application built with FastAPI, async SQLAlchemy, PostgreSQL, and server-rendered Jinja templates. It includes a REST API and browser UI for accounts, posts, profile images, and password recovery.

Demo status: the former public demo is offline because the GitHub Student hosting credits that funded its DigitalOcean droplet expired. The source, migrations, and automated tests remain available here.

Features

  • Account registration and login with OAuth2 password flow, JWT access tokens, and Argon2 password hashing.
  • Post creation, reading, full/partial updates, deletion, authorization checks, and pagination.
  • Profile management with validated image processing and AWS S3 or S3-compatible object storage.
  • Email password recovery using short-lived, single-use reset tokens stored only as hashes.
  • Jinja2 server-rendered pages backed by the same application and data layer as the JSON API.
  • Health endpoint, structured validation, and separate HTML/API error handling.

Architecture

Browser / API client
        |
FastAPI routers + Jinja2 views
        |
Async SQLAlchemy sessions
        |
PostgreSQL (production and automated tests)

Supporting services: S3 object storage and SMTP email

The application is split into routers for users and posts, Pydantic schemas for request/response validation, SQLAlchemy models for persistence, and Alembic migrations for schema changes. Blocking S3 calls run outside the async event loop.

Security and deployment

The original deployment ran on a hardened Ubuntu VPS with a non-root deploy user, SSH key-only access, UFW, fail2ban, unattended security upgrades, and Nginx terminating TLS before proxying to FastAPI. Application-level controls include Argon2 password hashing, signed JWTs, ownership checks on mutations, upload type/size validation, and hashed expiring password-reset tokens.

The old public URL has intentionally been removed rather than pointing visitors to an unavailable deployment.

Local development

Requirements: Python 3.14+, uv, and credentials for an S3 bucket or compatible local service if you want to exercise image uploads.

git clone https://github.com/ieraasyl/fastapi-blog.git
cd fastapi-blog
cp .env.example .env
uv sync
uv run alembic upgrade head
uv run fastapi dev

The app starts at http://localhost:8000; FastAPI's API documentation is available at /docs. The example configuration uses SQLite for a zero-setup local database. Replace DATABASE_URL with an async PostgreSQL URL for production-like development.

Tests and quality checks

The pytest configuration expects PostgreSQL on localhost:5432, with database test_blog, user postgres, and password secret. One disposable local setup is:

docker run --rm --name fastapi-blog-test-postgres \
  -e POSTGRES_PASSWORD=secret \
  -e POSTGRES_DB=test_blog \
  -p 5432:5432 \
  -d postgres:17-alpine
uv run python -m pytest
uv run ruff check .
uv run ty check

Stop the disposable database when finished:

docker stop fastapi-blog-test-postgres

The async pytest suite covers registration, duplicate-account handling, authentication, post CRUD, authorization, pagination, password-reset dispatch, and S3 image uploads. Tests isolate database work and use moto rather than contacting AWS.

Database migrations

Create and apply Alembic revisions after model changes:

uv run alembic revision --autogenerate -m "describe change"
uv run alembic upgrade head

Configuration

All settings are loaded from environment variables or .env; see .env.example. Never commit production credentials, JWT secrets, SMTP passwords, or cloud access keys.

About

just a simple blog project

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages