Project Setup & Configuration
MinIO vs AWS S3
This project uses MinIO, an open-source self-hosted object storage server that is fully compatible with the S3 API. You can run and test everything locally without an AWS account or cloud costs.
What is an object storage server?
Basically it’s just a place on the web to store files, and then get publicly accessible URLs for those files. The most popular way to do this is with Amazon’s S3 service. But this service has become so popular that many other services have implemented the S3 API. For instance, Minio, Backblaze B2, and Cloudflare C2 all implement the S3 API.
To switch to AWS S3 later, you only need to update your configuration:
- Change
S3_ENDPOINT_URLto the AWS S3 endpoint (or remove it to use the default) - Set
AWS_ACCESS_KEYandAWS_SECRET_KEYto your AWS IAM credentials - Make sure your
BUCKET_NAMEexists in your AWS account
No code changes required — MinIO implements the same API as AWS S3.
Starting the Services
The project uses Docker Compose to run MinIO and PostgreSQL locally:
cd backend
docker compose up -d
This starts:
- MinIO on port
9000(S3 API) and9001(web console) - PostgreSQL on port
5432
You can access the MinIO web console at http://localhost:9001 to browse uploaded files.
Backend Setup
Follow these steps to setup the backend FastAPI server.
cd backend
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp .env.sample .env
Edit .env with your MinIO credentials (the defaults from docker-compose.yml
will work for local development), then start the API:
fastapi dev
The API will be available at http://localhost:8000. Visit
http://localhost:8000/docs for the interactive API docs.
Frontend Setup
Run this to setup the frontend. This frontend uses vite with the React TypeScript template.
cd frontend
npm install
npm run dev
The frontend will be available at http://localhost:5173.
Configuration with Environment Variables
All sensitive configuration lives in config.py, which reads from environment
variables so secrets are never hard-coded in source:
# config.py
import os
from dotenv import load_dotenv
success = load_dotenv()
if not success:
print("Warning: .env file not found or couldn't be loaded.")
# Database
DATABASE_URL = os.environ.get(
"DATABASE_URL",
"postgresql+psycopg://postgres:postgres@localhost:5432/photos",
)
# S3 / MinIO
AWS_ACCESS_KEY = os.environ.get("AWS_ACCESS_KEY")
AWS_SECRET_KEY = os.environ.get("AWS_SECRET_KEY")
S3_PUBLIC_URL = os.environ.get("S3_PUBLIC_URL", "http://localhost:9000")
S3_ENDPOINT_URL = os.environ.get("S3_ENDPOINT_URL", "http://localhost:9000")
BUCKET_NAME = "photos"
if AWS_ACCESS_KEY is None or AWS_SECRET_KEY is None:
msg = "AWS_ACCESS_KEY and AWS_SECRET_KEY must be defined in .env file."
raise ValueError(msg)
# CORS — comma-separated origins
cors_origins_value = os.environ.get("CORS_ORIGINS", "http://localhost:5173")
CORS_ORIGINS = cors_origins_value.split(",")
What’s that CORS business?
CORS stands for Cross Origin Resource Sharing. JavaScript inside your browser can only access URLs on the same domain by default. We need CORS here because our frontend and backend are running on different ports. This allows the frontend running on port 5173 to access the backend FastAPI server running on 8000.
Wait, what even are environment variables?
Think of environment variables as operating system level variables.
When you run your program on different computers, these are values that
need to change when you are running on a different machine. Often we
store secret values here, because they will need to be different between
our dev environment and our production environment. A handy way to set them
is to use the dotenv package, which reads them from a .env file that
we list in our gitignore so we don’t accidentally check them in.
The .env file
Copy .env.sample to .env and fill in your values:
AWS_ACCESS_KEY=minioadmin
AWS_SECRET_KEY=minioadmin
S3_ENDPOINT_URL=http://localhost:9000
S3_PUBLIC_URL=http://localhost:9000
DATABASE_URL=postgresql+psycopg://postgres:postgres@localhost:5432/photos
CORS_ORIGINS=http://localhost:5173
The .env file should be in .gitignore — never commit it.