Guide
Next.js AI App Architecture
How Agentic Academy Labs structures Next.js AI apps in Sikar: App Router, Bun APIs, Postgres, RAG, Flutter clients, and what we build in week 1.
Published 2026-08-08 · Updated 2026-08-14
The AI apps we ship from Agentic Academy Labs share one shape: Next.js App Router for the product UI, a Bun-backed API that never leaks keys to the browser, PostgreSQL for app state and often pgvector for retrieval, and Flutter when there is a mobile surface. Interns learn this split on day one because mixing prompts into React components is how demos rot. The architecture below is what we use for client work, not a textbook diagram.
Layers we actually keep separate
- Presentation: Next.js server components for shells and lists; client components only for chat, streaming, and file upload.
- API: Route Handlers (or a Bun service) that authenticate, rate-limit, and call orchestration. Flutter hits the same HTTP contract.
- Orchestration: retrieve, assemble messages, call tools, generate. One function signature so we can swap OpenAI and Anthropic. See OpenAI vs Anthropic for production in India.
- Data: PostgreSQL for users, threads, and audit rows. pgvector or a vector store for chunks, always filtered by tenant.
- Providers: model adapters behind an interface. Brand names stay out of UI code.
- Observability: structured logs for tokens, latency, retrieved ids, and eval scores.
Reference request flow
- User sends a message from the Next.js chat client or the Flutter app.
- API validates session and quota. We fail closed if org_id is missing.
- Retriever fetches top-k chunks for that tenant only.
- Orchestrator builds system prompt plus context plus user input. Context is capped; we do not dump the whole handbook.
- Model stream returns tokens. The UI renders as they arrive. The server writes the full turn when the stream ends.
- A background job scores a sample of turns against the golden set so quality regressions show up before Slack complaints.
Week 1 on a typical engagement
- Stand up Next.js + Bun + Postgres locally (we run local Postgres first, then the hosted project). Auth and one protected chat route, nothing else.
- Put provider keys in server env only. A PR that imports an SDK in a client component is rejected in review.
- Write 15 eval questions before the first pretty UI. Interns own the spreadsheet; engineers own the logger.
- Stream a stubbed answer so Flutter and web share the same event shape early.
- Add tenant_id on every retrieval query, even if there is only one customer this week.
Cost and latency knobs that matter
| Knob | What we do | What goes wrong if you skip it |
|---|---|---|
| Context size | Cap chunks; fix chunking before buying a bigger model | Token bills climb and answers get worse |
| Model split | Small model for routing, larger for the final answer | You pay frontier prices for 'is this on-topic?' |
| Cache | Cache embeddings and hot retrieval per tenant | Every greeting re-embeds the same PDF |
| Region | Deploy the app near users; keep Postgres warm | IST p95 looks fine in a US region demo and dies at 11am |
Security checklist we treat as blocking
- No provider keys in NEXT_PUBLIC_* or Flutter dart-define for production secrets.
- Retrieval filters include tenant (and often role) before similarity search.
- Tool outputs are treated as untrusted text, not HTML.
- Per-user rate limits exist before the first public link. Internship demos have been scraped.
- Audit log of prompts and sources for any product that touches policy or money.
Failure modes from our lab and clients
- Server Actions that call the model with no timeout. One hung provider stalls the whole page.
- Storing chat history only in React state. Refresh loses the thread; Flutter and web disagree.
- Embedding on every upload on the request path. Large PDFs time out on Vercel; we moved ingest to a queue.
- One shared prompt file with no version. You cannot explain why Tuesday's answers got worse.
If you are leaving a PHP or Rails monolith, pair this with migrate a monolith to Next.js and PostgreSQL. If the feature is document Q&A, start from RAG vs fine-tuning. Agentic Academy Labs ships this stack under AI web and mobile development and custom AI development. Schedule a call if you want the same skeleton we use in Sikar.
Frequently asked questions
- Where should API keys live in a Next.js AI app?
- On the server only: Route Handlers, a Bun service, or server actions. Never in the browser or a Flutter client for production provider keys.
- Do you stream responses?
- Yes. Streaming is the default UX. The server still logs the full turn, tokens, sources, and latency when the stream completes.
- Why PostgreSQL instead of a dedicated vector database on day one?
- Postgres already holds users and threads. pgvector is enough for many MVPs. We split the vector store when recall, scale, or ops demand it, not because a blog recommended it.
- Can Flutter share this architecture?
- Yes. Flutter talks to the same authenticated API. We do not duplicate orchestration in Dart.