Skip to content
VALERAWEBDEV07
AINovember 7, 20263 min read

Inside AI OPS: how to replace a background in 20 seconds without breaking the product

A look inside AI OPS: the async pipeline, Supabase, fal.ai, limits, polling and deployment on Caddy + systemd.

ShareTelegram
Back to blog
Contents

Why this is harder than "just replace the background"

At first glance the task sounds simple: take a product photo, remove the background, generate a new one. In practice, generic generators break logos, seams, textures and small text. For an online store, that is not an "ugly image" — it is a return, a lost customer and lost money.

AI OPS is not a image generator. It is a pipeline where the product stays untouched and only the environment changes. Here is how it works and which decisions mattered.

What the service does

The user uploads a product photo, picks or describes a scene and gets a new background. The demo is free, limited and available in three languages. Generation takes about 20 seconds and costs around $0.02 per run.

Under the hood: Next.js 16, Supabase, fal.ai and Telegram for lead notifications.

Architecture

Browser → Next.js API → Supabase → fal.ai → Telegram
  • The browser sends a photo and a scene description.
  • Next.js stores the request and the pipeline run in Supabase.
  • fal.ai generates the new background.
  • The result is saved to pipeline_runs.result_url.
  • Telegram receives a new lead notification.

The key idea: the API does not wait for generation. It responds immediately and the work continues in the background.

Decision 1. A queue without extra infrastructure

For an MVP I did not want Redis, workers and a separate queue service. Next.js has after() — a function that runs after the response is sent:

after(async () => {
  await processPipelineRun(runId);
});

The user receives a runId immediately while the pipeline keeps working. If generation fails, the status becomes error and the limit is not consumed.

Decision 2. Polling that survives a reload

The client polls /api/relight/[id] every two seconds. The runId is stored in localStorage, so even after a page reload the client can keep waiting for the result.

On a network error the client does not show "everything is broken" — it reconnects. A small detail that makes the product feel reliable.

Decision 3. Limits that do not reset on errors

The demo has limits: two generations per email and three per IP. An unsuccessful run should not consume an attempt. Otherwise a user hits a random error and can no longer try.

Limits are counted atomically through a keyed mutex, which protects against races when several requests arrive at once.

Decision 4. Three languages and production

The interface and legal pages are translated into en, ru and uk. The language is detected from the browser, and the manual choice is saved.

Production lives on the same server: Caddy handles HTTPS, systemd keeps Next.js running, a health check monitors the app, and deploys use an atomic .next swap with rollback on failure.

What I would improve next

  • Move the pipeline to a separate worker if load grows.
  • Add a queue with retries and deadlines.
  • Show generation progress instead of only a spinner.
  • Add a before/after comparison directly in the result.

Bottom line

AI OPS is an example of an AI product built not on a single model, but on the engineering around it: limits, failure recovery, result storage, localization and deployment. The model can be replaced. A reliable pipeline is harder.

ShareTelegram
Back to blog

Валера · webdev07

Senior Fullstack + AI. I build systems that work.

@webdevs07

Need similar automation?

Message me on Telegram — let's discuss the task and the format.

Discuss a project