Multi-Model AI Chatbot - React, Vite, TypeScript, Prisma, PostgreSQL Full-Stack Project (including Insights & Performance Dashboard)
A modern, responsive AI chat bot application supporting multiple AI providers including Google Gemini, Groq, OpenRouter, Hugging Face, and OpenAI and enable to store the chat history. Built with React, TypeScript, and Vite including business-insights analytics and performance dashboard, typewriter effect, and animated icons for the best user experience.
- Live-Demo: https://multi-ai-chat-hub.vercel.app/
- Security: Private reports → SECURITY.md · contact@arnobmahmud.com
- Author: Arnob Mahmud | LinkedIn: https://www.linkedin.com/in/arnob-mahmud-05839655/ | GitHub: https://github.com/arnobt78
- Overview
- Keywords
- Features
- How the App Works (Beginner Walkthrough)
- Technologies & Libraries
- Project Structure
- Installation
- Environment Variables (
.env) - How to Run
- Usage Guide
- Frontend Components & Hooks
- Shared AI Layer
- Backend API Endpoints
- Database (Prisma + PostgreSQL)
- Sentry (Optional Observability)
- Reusing Code in Other Projects
- Deployment (Vercel)
- Scripts Reference
- Further Reading
- Conclusion
- License
- Happy Coding!
AI Chat Hub (package name ai-chat-hub) is a Vite client-rendered SPA (not Next.js) with Vercel Serverless Functions under api/.
You can:
- Chat with several AI providers from one UI.
- Let the server auto-fallback across providers and models when one fails or rate-limits.
- Keep chat history in the browser (
localStorage) — no login required for chatting. - Open a Business Insights dashboard backed by Prisma + PostgreSQL (anonymous analytics).
Important architecture idea for learners: AI API keys stay on the server (GEMINI_API_KEY, etc.). The browser only calls POST /api/chat. That way secrets never appear in the Vite JavaScript bundle.
AI Chat Hub · multi-provider chatbot · Gemini · Groq · OpenRouter · Hugging Face · OpenAI · React 18 · Vite · TypeScript · Prisma · PostgreSQL · Coolify VPS · Vercel Serverless · Zod · Sentry tunnel · localStorage · auto fallback · Business Insights · OpenAI-compatible API
- Multi-provider support — Gemini, Groq, OpenRouter, Hugging Face, OpenAI
- Auto fallback — provider order: Groq → Gemini → OpenRouter → Hugging Face → OpenAI
- Within-provider model chains — try the next free-tier model on retriable errors; skip remaining models on HTTP 429
- Provider dropdown — availability comes from
GET /api/chat-providers(no secrets) - Chat history — multiple threads stored in
localStorage - Typing indicator — visual feedback while waiting for the AI
- Emoji picker —
@emoji-mart/react - Typewriter titles —
useTypewriterhook - Collapsible sidebar + tooltips
- Dark theme UI with gradient accents
- Business Insights dashboard — usage charts (Recharts), provider stats
- Anonymous session tracking — no user accounts
- Soft IP rate limits on chat / events / Sentry tunnel
- Optional Sentry with same-origin tunnel
POST /api/monitoring(ad-blocker friendly) - Security headers + robots.txt via
vercel.json/public/robots.txt
Think of three layers:
Browser (React + Vite)
│ POST /api/chat { message, provider? }
▼
Vercel Function (api/chat.ts)
│ reads server env keys → shared/ai/orchestrate.ts
▼
Upstream AI APIs (Groq / Gemini / OpenRouter / HF / OpenAI)
App.tsxis a tiny view state machine:"start"|"chat"|"insights"(no React Router).ChatBotStartis the landing screen; “Get Started” switches to chat.ChatBotAppmanages messages, sidebar chats, provider selection, and callsaiService.getChatResponse().aiService.tsonly talks to/api/chat— it never holds API keys.shared/ai/orchestrate.tstries providers/models, returns{ content, provider, success }.- Analytics POSTs go to
/api/events; the Insights UI reads/api/dashboard(and related routes).
Local tip: Plain npm run dev (Vite alone) does not serve /api/*. Prefer vercel dev so chat and analytics work like production.
| Technology | Version (approx.) | What it is / why we use it |
|---|---|---|
| React | 18.3 | UI components and hooks |
| TypeScript | 5.9 | Static types — fewer runtime surprises |
| Vite | 7.3 | Fast dev server + production bundler for SPAs |
| Node.js | 24.x | Runtime pinned in package.json engines + .nvmrc |
| Prisma | 6.19 | Type-safe ORM for PostgreSQL analytics |
| PostgreSQL (Coolify VPS) | — | Insights analytics DB (not chat history) |
| Zod | 4.x | Runtime validation of API request bodies |
| Vercel Functions | api/*.ts |
Backend without a separate Express server |
| Recharts | 2.x | Charts on the Insights dashboard |
| Lucide React | — | Icons |
| Emoji Mart | — | Emoji picker |
| uuid | 11 | Chat / session IDs |
| Sentry | optional | Error monitoring + tunnel |
| ESLint 9 | flat config | npm run lint |
Example — thin client chat call:
// src/services/aiService.ts (concept)
const response = await fetch("/api/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ message, provider }),
});Example — reusable typewriter hook:
const { displayText } = useTypewriter({
text: "Welcome to AI Chat Hub",
speed: 50,
delay: 300,
});multi-ai-chatbot/
├── api/ # Vercel serverless functions (backend)
│ ├── _lib/
│ │ ├── prisma.ts # Prisma client singleton
│ │ └── rateLimit.ts # Soft in-memory IP rate limit
│ ├── chat.ts # POST /api/chat — AI proxy
│ ├── chat-providers.ts # GET /api/chat-providers — availability
│ ├── events.ts # POST /api/events — analytics write
│ ├── usage.ts # GET /api/usage
│ ├── insights.ts # GET /api/insights
│ ├── providers.ts # GET /api/providers
│ ├── dashboard.ts # GET /api/dashboard
│ └── monitoring.ts # POST /api/monitoring — Sentry tunnel
├── shared/
│ ├── ai/ # Types, registry, callers, orchestrate, Zod
│ └── sentry/ # Env helpers, filters, server capture
├── prisma/
│ └── schema.prisma # Event, Session, ProviderStats
├── public/
│ ├── ai.svg
│ ├── chatbot.svg
│ ├── favicon.ico
│ └── robots.txt
├── src/
│ ├── App.tsx # start | chat | insights
│ ├── main.tsx # React root + Sentry ErrorBoundary
│ ├── sentry.ts # Client Sentry.init (tunnel)
│ ├── Components/ # UI + CSS
│ ├── hooks/useTypewriter.ts
│ └── services/ # Thin client wrappers
├── docs/ # Portable guides (LLM, Sentry, Vercel, Agile V)
├── .env.example # Env template (copy → .env)
├── vercel.json # Security + cache headers
├── vite.config.ts
├── eslint.config.js
├── package.json
├── SECURITY.md
└── README.md
- Node.js 24.x (see
.nvmrc) - npm (comes with Node)
- Optional: Vercel CLI (
npm i -g vercel) forvercel dev - Optional: free accounts for AI providers + Coolify Postgres (Insights) + Sentry
# Clone
git clone https://github.com/arnobt78/OpenAI-ChatBot--ReactVite.git
cd OpenAI-ChatBot--ReactVite
# Use Node 24 if you use nvm
nvm use
# Install dependencies
npm install
# Copy env template
cp .env.example .env
# Then edit .env — see next sectionCopy .env.example to .env. Never commit .env (it is gitignored).
| Goal | Need .env? |
|---|---|
UI only (npm run dev) — landing / layout |
No — app boots without keys |
| Real AI chat locally | Yes — at least one AI key + use vercel dev |
| Business Insights charts | Yes — DATABASE_URL + Prisma push |
| Sentry errors | Optional — leave DSN empty to disable |
You can start with an empty .env for UI exploration; add keys as you enable features.
| Variable | Purpose | Where to get it |
|---|---|---|
GEMINI_API_KEY |
Google Gemini | Google AI Studio |
GROQ_API_KEY |
Groq | Groq Console |
OPENROUTER_API_KEY |
OpenRouter free models | OpenRouter Keys |
HUGGINGFACE_API_KEY |
HF Inference Providers | HF Tokens — allow Inference Providers |
OPENAI_API_KEY |
OpenAI (paid last resort) | OpenAI API Keys |
You need at least one of the above for chat. More keys = better fallback coverage.
GEMINI_API_KEY=
GROQ_API_KEY=
OPENROUTER_API_KEY=
HUGGINGFACE_API_KEY=
OPENAI_API_KEY=
APP_URL=https://multi-ai-chat-hub.vercel.appSecurity lesson: Never put AI secrets in
VITE_*variables. Vite embedsVITE_*into the public JS bundle — anyone could steal them.
| Variable | Purpose | Where to get it |
|---|---|---|
DATABASE_URL |
PostgreSQL connection string | Coolify VPS Postgres (or any Postgres) connection string |
npx prisma generate
npx prisma db push| Variable | Purpose | Where to get it |
|---|---|---|
VITE_SENTRY_DSN |
Client SDK (build-time) | Sentry → Project → Client Keys (DSN) |
SENTRY_DSN |
Server + tunnel allowlist | Same DSN (optional) |
SENTRY_ORG |
Source map upload | Organization slug |
SENTRY_PROJECT |
Source map upload | Project slug (not org name) |
SENTRY_AUTH_TOKEN |
CI upload | Auth Tokens (project:releases, org:read) |
Use VITE_SENTRY_DSN, not NEXT_PUBLIC_SENTRY_DSN (that is Next.js-only). On Vercel, set VITE_SENTRY_DSN for Production build.
Full comments live in .env.example. Deeper Sentry steps: docs/Redis_Sentry_PostHog_INTEGRATION_GUIDE.md (§2B Vite).
npm run devOpens the Vite app. Chat/API calls will fail until serverless routes are available.
# Terminal: serves Vite + /api/* together
vercel devThen open the URL Vercel prints (often http://localhost:3000).
npm run lint
npm run build
npm run preview # preview the dist/ folder only (still no /api unless proxied)- Open the app → Start screen with typewriter title.
- Click Get Started → chat view.
- Type a message (optional emoji) → send.
- Pick a provider from the dropdown, or leave auto/fallback behavior.
- Create / switch / delete chats in the sidebar (persisted in
localStorage). - Open Business Insights for anonymous analytics (needs
DATABASE_URL).
| File | Role | Reuse tip |
|---|---|---|
ChatBotStart.tsx |
Welcome / CTA | Drop into any landing; wire onStart |
ChatBotApp.tsx |
Main chat shell | Expects chat list props or lift state like App.tsx |
BusinessInsights.tsx |
Analytics dashboard | Point fetch URLs at your /api/dashboard |
TypingIndicator.tsx |
Animated “AI is typing” | Pure UI — no API |
Tooltip.tsx |
Hover help | Wrap any trigger element |
useTypewriter.ts |
Character-by-character text | Any headline / onboarding copy |
Each component has a matching .css file — keep them together when copying.
View switching (App.tsx concept):
const [currentView, setCurrentView] = useState<"start" | "chat" | "insights">(
"start",
);
// render ChatBotStart | ChatBotApp | BusinessInsightsLocated in shared/ai/ so browser types and server orchestration share one contract.
| Module | Purpose |
|---|---|
types.ts |
AIProvider, ChatRequest, ChatResponse, ProviderMeta |
providers.ts |
PROVIDER_META model chains + FALLBACK_ORDER |
callers.ts |
Upstream HTTP + stream* token generators |
orchestrate.ts |
Provider/model loops; orchestrateChatStream for SSE |
stream.ts |
OpenAI-compat / Gemini SSE parsers |
schemas.ts |
Zod schemas for request validation |
Current free-tier model chains (see shared/ai/providers.ts):
- Groq —
openai/gpt-oss-20b→openai/gpt-oss-120b→qwen/qwen3.6-27b - Gemini —
gemini-2.5-flash→gemini-2.5-flash-lite - OpenRouter —
openai/gpt-oss-20b:free→openai/gpt-oss-120b:free - Hugging Face — Hub chat IDs +
:fastest(gemma / Qwen2.5 / gpt-oss / Llama-3.2); free credits tiny — may fail when forced - OpenAI —
gpt-4o-mini(last resort)
Portable free-tier reference: docs/LLM_MODEL_SELECTION.md.
All handlers live in api/ and use @vercel/node request/response shapes.
| Method | Path | Purpose |
|---|---|---|
POST |
/api/chat |
Chat proxy (Zod + rate limit; JSON or SSE stream:true) |
GET |
/api/chat-providers |
Which providers have keys configured |
POST |
/api/events |
Write anonymous analytics events |
GET |
/api/usage |
Usage aggregates |
GET |
/api/insights |
Provider insight data |
GET |
/api/providers |
Provider detail stats |
GET |
/api/dashboard |
Combined dashboard payload |
POST |
/api/monitoring |
Sentry envelope tunnel (ad-blocker bypass) |
Example chat body:
{ "message": "Explain React hooks in one paragraph", "provider": "groq" }Omit provider (or use auto) to walk the fallback order.
There is no React Router — “routes” are view states in App.tsx, plus these HTTP APIs.
Schema: prisma/schema.prisma
Session— anonymous browser sessionEvent—api_call,chat_created,provider_selected, etc.ProviderStats— aggregated provider metrics
Chats themselves are not stored in Postgres by default — only analytics. Chat threads use localStorage.
- Client:
src/sentry.ts→tunnel: "/api/monitoring" - Server:
captureApiExceptioninapi/chat.ts/api/events.ts - Quiet builds:
@sentry/vite-pluginwithsilent: truewhen org/project/token are set
Disabled automatically when DSN is empty.
- Copy
shared/ai/into another Node/Vite/Next backend and callorchestrateChatfrom your route. - Copy
useTypewriter+TypingIndicator/Tooltipas standalone UI pieces. - Copy
api/_lib/rateLimit.tsfor soft serverless rate limiting. - Copy Sentry §2B from the integration guide for another Vite app.
- Keep AI keys server-side; expose only a thin
/api/chat-style proxy.
When teaching others: stress the registry (providers.ts) + callers + orchestrator pattern so model deprecations become a one-line registry edit.
- Import the GitHub repo into Vercel.
- Set env vars (same names as
.env.example) — especially non-VITE_AI keys andDATABASE_URL. - For Sentry client events, set
VITE_SENTRY_DSNon Production (build-time). - Deploy. Live demo pattern: https://multi-ai-chat-hub.vercel.app/
- Dashboard Human-Action (recommended): Bot Protection = Challenge, AI Bots = Deny.
Production guardrails playbook: docs/VERCEL_PRODUCTION_GUARDRAILS.md.
| Script | Command | Purpose |
|---|---|---|
| Dev (UI) | npm run dev |
Vite only |
| Lint | npm run lint |
ESLint (max warnings = 0) |
| Build | npm run build |
prisma generate + tsc + vite build |
| Preview | npm run preview |
Serve dist/ |
| Prisma | npm run prisma:generate / prisma:push / prisma:studio |
DB tooling |
- docs/LLM_MODEL_SELECTION.md — free-tier models & fallback strategy
- docs/Redis_Sentry_PostHog_INTEGRATION_GUIDE.md — Next and Vite Sentry
- docs/VERCEL_PRODUCTION_GUARDRAILS.md — headers, AI proxy, Node 24
- docs/AGILE_V_PROTOCOL.md — agent workflow used on this repo
- SECURITY.md — private vulnerability reporting
This project is a practical classroom for multi-provider AI apps on Vite + Vercel: keep secrets on the server, validate with Zod, fall back across models, store chat locally, and optionally measure usage with Prisma. Clone it, add one free API key, run vercel dev, and you will see the full loop from UI → /api/chat → upstream model → response.
Extend it by adding providers to shared/ai/providers.ts, reusing UI components, or plugging the shared orchestrator into another backend.
This project is licensed under the MIT License. Feel free to use, modify, and distribute the code as per the terms of the license.
This is an open-source project - feel free to use, enhance, and extend this project further!
If you have any questions or want to share your work, reach out via GitHub or my portfolio at https://www.arnobmahmud.com/.









