How I Built an AI Photo Restoration App with Next.js, Supabase, and Replicate
A few months ago I dug out a box of family photos from the 80s. Most were faded, scratched, or had those crease marks where they'd been folded for decades. I tried fixing them in Photoshop, but I had dozens of them and gave up after the second one. So I did what any developer would do: I built a tool for it. This post is a technical walkthrough of what I learned building PixRestorer — an AI photo…
Several months ago, the author uncovered a collection of family photographs from the 1980s. Many of these photos were deteriorated, scratched or had crease marks due to repeated folding over the years. The author attempted to restore them using Photoshop but quickly became overwhelmed by the task. The desire to build a tool for photo restoration led to the creation of PixRestorer, an AI-powered web application that repairs damaged, blurry and black-and-white photos within 30 seconds.
The article outlines the technical aspects and architectural decisions made while building this application. The stack used includes Next.js 16 (App Router, TypeScript), Supabase (Postgres + Auth), Cloudflare R2 (image storage), Replicate (AI models), Cloudflare Workers (deployment via @opennextjs/cloudflare), Waffo/Creem (payments).
One of the key architectural decisions the author made was to keep the application stateless, with all dependencies residing outside the app. This allowed for trivial deployment, as there is no Node runtime to manage, no image processing locally (Cloudflare Image Resizing handles it), and rollbacks are accomplished by merely changing DNS settings.
The author faced three main challenges when transitioning to Cloudflare Workers with OpenNext: middleware naming, request body limits, and rate limiting. Middleware naming required keeping the old middleware.ts filename for Supabase session-refresh logic to work on Workers. Request body limits necessitated testing early, as a 12MB image upload had to make it through the middleware unaltered.
The author abandoned in-memory rate limiting due to its unreliability on Workers, instead opting to use credit balances as a form of rate limiting, as every image upload already incurs fees via atomic PostgreSQL remote procedure calls (RPCs). The restoration pipeline comprising several steps, each designed to solve specific issues the author encountered during development.
Input validation ensures SSRF protection by requiring any URLs to come from a pre-signed upload URL within the author's own R2 bucket. Credit deduction is performed atomically using a SQL UPDATE statement, ensuring successful deduction only when the user's balance is sufficient and returning NULL if insufficient funds are available.
Failure handling is another critical aspect of the system, where the app refunds the user's credit immediately upon encountering model errors. Uploaded originals are stored temporarily in R2 and deleted regardless of the outcome to prevent slow, invisible cost leaks. The system incorporates moderation before the AI model runs, preventing policy violations from incurring costs.
The cost of running the application relies on Replicate's per-call pricing for the AI model. By using prepaid credits that are atomically deducted and refunded on failure, the author ensures that users never pay for failed requests, and the author never bears the cost of successful ones.
Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.