Slip Scanner Backend (FastAPI)
FastAPI service exposing a /scan endpoint to process receipt images:
- Validates Supabase user via Authorization: Bearer
- Detects edges and crops the receipt (OpenCV, perspective transform)
- Performs OCR via Hugging Face Inference API (TrOCR)
- Uploads the cropped image to Supabase Storage
- Inserts a row into the slips table with extracted metadata
Prerequisites
- Python 3.11+
- Supabase project with Storage and Postgres
- Hugging Face account and API token
Environment Variables
Create a .env file in backend/ with:
SUPABASE_URL=... SUPABASE_ANON_KEY=... SUPABASE_SERVICE_ROLE_KEY=... SUPABASE_STORAGE_BUCKET=slips
HUGGINGFACE_API_TOKEN=... HF_OCR_MODEL=microsoft/trocr-base-printed
FRONTEND_CORS_ORIGIN=http://localhost:3000
Notes:
- Use the Service Role key server-side only. Never expose it to the frontend.
- If you prefer signed URLs, adjust upload_bytes_to_supabase_storage accordingly and do not make the bucket public.
Install & Run (Windows PowerShell)
cd backend python -m venv .venv . .venv\Scripts\Activate.ps1 pip install -r requirements.txt uvicorn backend.main:app --reload --host 0.0.0.0 --port 8000
API
Health
GET http://localhost:8000/health
Scan
POST http://localhost:8000/scan Authorization: Bearer Content-Type: multipart/form-data file=@/path/to/receipt.jpg
cURL example:
curl -X POST http://localhost:8000/scan
-H "Authorization: Bearer $SUPABASE_JWT"
-F "file=@receipt.jpg"
Response: { "ok": true, "slip": { "id": "...", "user_id": "...", "image_url": "https://...", "amount": 250.0, "category": "Food", "raw_text": "...", "created_at": "2025-09-15T10:00:00Z", "crop_confidence": 0.8 } }
Supabase Setup
Storage Bucket (public or signed)
- Create bucket slips.
- For public previews, enable public read on the bucket or a specific folder per user. Alternatively, keep private and generate signed URLs server-side.
Table
Create table slips (adjust types as desired):
create table if not exists public.slips ( id uuid primary key default gen_random_uuid(), user_id uuid not null, image_path text not null, image_url text, raw_text text, amount numeric, category text, crop_confidence real, created_at timestamptz not null default now() );
alter table public.slips enable row level security;
RLS policies (only owner reads/writes their slips):
create policy "slips select own" on public.slips for select using (auth.uid() = user_id);
create policy "slips insert own" on public.slips for insert with check (auth.uid() = user_id);
create policy "slips update own" on public.slips for update using (auth.uid() = user_id);
Frontend Config
In Next.js .env.local:
NEXT_PUBLIC_API_URL=http://localhost:8000 NEXT_PUBLIC_SUPABASE_URL=... NEXT_PUBLIC_SUPABASE_ANON_KEY=...
From the client, include the Supabase session JWT as the Authorization header when calling /scan.
Design Notes (rules.md alignment)
- Mobile-first UX is handled in the Next.js app; backend is stateless and fast.
- No sensitive keys in frontend; backend uses Service Role for Storage/DB.
- Performance: edge detection and OCR happen server-side; images are resized and optimized.
- Security: RLS ensures users only see their own slips; Storage access can be scoped.
Dev Tips
- OpenAPI docs: http://localhost:8000/docs
- If HF model cold-starts and returns 503, retry the request after a few seconds.