Building a Text-to-Image Pipeline with Next.js — Architecture Decisions A developer building Pixova's free AI image generator implemented an async job queue pattern to connect a Next.js frontend to a GPU inference endpoint, avoiding timeout issues from long-running HTTP connections. The architecture returns a job ID immediately and polls for status every two seconds, trading real-time updates for simpler implementation suitable for an early-stage product. When I built the backend for free AI image generator no credit card https://pixova.io , the core challenge was connecting a Next.js frontend to a GPU inference endpoint efficiently. Here's the architecture thinking — not the specific implementation, but the decisions that shaped it. Text-to-image generation has an unusual request profile: The solution: async job queue pattern. User submits prompt → Job created in queue → Job ID returned immediately → Frontend polls for status → Result delivered when ready // app/api/generate/route.js export async function POST request { const { prompt } = await request.json ; // BAD: holds connection open for 15+ seconds const image = await runInference prompt ; return NextResponse.json { image } ; } Problem: HTTP connections time out. Vercel serverless functions have execution limits. One slow generation blocks the connection. // app/api/generate/route.js — Submit job export async function POST request { const { prompt } = await request.json ; // Submit to inference queue, get job ID immediately const jobId = await submitToQueue { prompt } ; // Return immediately — don't wait for completion return NextResponse.json { jobId, status: 'processing' } ; } // app/api/status/ jobId /route.js — Check status export async function GET request, { params } { const { jobId } = params; const result = await checkJobStatus jobId ; return NextResponse.json { status: result.status, // 'processing' | 'completed' | 'failed' image: result.image ?? null, } ; } Frontend polls /api/status/ jobId every 2 seconds until complete. // Real-time updates via WebSocket // Backend notifies frontend when job completes // No polling overhead // More infrastructure to maintain For an early-stage product, Option B polling is the right tradeoff — simpler to implement and debug, good enough UX. python 'use client'; import { useState, useCallback } from 'react'; import Image from 'next/image'; export default function ImageGenerator { const prompt, setPrompt = useState '' ; const status, setStatus = useState 'idle' ; // idle | submitting | processing | complete | error const imageUrl, setImageUrl = useState null ; const generate = useCallback async = { if prompt.trim || status === 'processing' return; setStatus 'submitting' ; try { // Submit job const submitRes = await fetch '/api/generate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify { prompt } , } ; const { jobId } = await submitRes.json ; setStatus 'processing' ; // Poll for result await pollForResult jobId ; } catch { setStatus 'error' ; } }, prompt, status ; const pollForResult = async jobId = { const maxAttempts = 60; // 2 min timeout for let i = 0; i < maxAttempts; i++ { await new Promise r = setTimeout r, 2000 ; const statusRes = await fetch /api/status/${jobId} ; const data = await statusRes.json ; if data.status === 'completed' { setImageUrl data.image ; setStatus 'complete' ; return; } if data.status === 'failed' { setStatus 'error' ; return; } } setStatus 'error' ; // Timeout }; return