Image Uploads: FastAPI + React + S3
This is a walk-through of a full-stack image upload system. It uses secure file validation, S3-compatible object storage, and two different React upload interfaces — a simple file picker and a drag-and-drop component with preview. The complete code is available on GitHub.
Who is this for?
If you know Python, TypeScript and FastAPI but don’t know how to integrate image uploads, this walk-through is for you!
Quick Start
Follow these steps if you just want to get the project up and running to see how the whole thing operates.
Clone and setup
git clone https://github.com/bartdorsey/image-upload-demo.git
cd image-upload-demo
Run the Backend
cd backend
docker compose up -d
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp .env.sample .env
fastapi dev
Run the Frontend
You’ll want to run this in a second terminal
cd frontend
npm install
npm run dev
You can access these in your web browser
- Frontend: http://localhost:5173
- Backend API: http://localhost:8000/docs
- MinIO Console: http://localhost:9001
Architecture Overview
This is a high level diagram showing how all the pieces fit together
graph LR
subgraph Frontend["React Frontend"]
A["• Upload Forms<br/>• Photo Gallery"]
end
subgraph Backend["FastAPI Backend"]
B["• File Validation<br/>• S3 Integration<br/>• Database"]
end
subgraph Storage["MinIO Storage"]
C["• Image Storage<br/>• Pre-signed URLs"]
end
subgraph Database["PostgreSQL"]
D["• Photo Metadata<br/>• Filenames"]
end
Frontend <--> Backend
Backend <--> Storage
Backend <--> Database
Prerequisites
- Backend: Python 3.11+, basic FastAPI knowledge
- Frontend: Node.js 18+, basic React and TypeScript knowledge
- General: Docker (for MinIO and PostgreSQL)
Learning Outcomes
After completing this walk-through you’ll understand:
- File upload handling in web APIs
- S3-compatible storage integration and pre-signed URLs
- Security best practices for file uploads
- Database design for file metadata
- Modern React file upload patterns including drag-and-drop
- Type-safe API integration with discriminated union result types
- CORS configuration for full-stack apps