A full-stack AI chatbot that lets you upload your own documents and ask questions about them. Built with Retrieval-Augmented Generation (RAG) — the AI only answers from your uploaded files, not from general knowledge.
- Upload documents — PDF, TXT, Markdown, JSON, and code files (JS, TS, Python, Go, etc.)
- Ask questions — The AI searches your documents and answers based only on what's in them
- See sources — Every answer shows which document it came from and how relevant it was
- Streaming responses — Answers appear word-by-word like ChatGPT
- User accounts — Sign up with email to keep your documents across devices and sessions
- Three answer modes — Normal, Simple (beginner-friendly), or Detailed (in-depth)
- Conversation memory — The AI remembers the last few messages in your session
| Layer | Technology |
|---|---|
| Frontend | React 18 + Vite |
| Backend | Node.js + Express |
| Database | Supabase (PostgreSQL + pgvector) |
| LLM | Groq API (llama-3.1-8b-instant) |
| Embeddings | Jina AI (jina-embeddings-v2-base-en, 768-dim) |
| Auth | Supabase Auth (email/password) |
| Streaming | Server-Sent Events (SSE) |
RAG/
├── backend/
│ ├── server.js # Express server entry point
│ ├── package.json
│ ├── .env.example # Environment variable template
│ ├── config/
│ │ └── database.js # Supabase client setup
│ ├── database/
│ │ ├── schema.sql # Initial database schema
│ │ ├── schema_v2_migration.sql # Adds file_type, upload_jobs, doc filtering
│ │ └── schema_v3_migration.sql # Adds user_id for per-user data isolation
│ ├── routes/
│ │ └── rag.js # All API endpoints
│ └── services/
│ ├── rag.js # RAG pipeline (embed → search → answer)
│ ├── embeddings.js # Jina AI embedding generation
│ ├── chunker.js # Document chunking (text + code-aware)
│ ├── documents.js # Document CRUD operations
│ ├── ingestion.js # Async file upload processing
│ └── llm.js # Groq LLM client
│
├── frontend/
│ ├── src/
│ │ ├── App.jsx # Main React app
│ │ ├── App.css # Sovereign Intelligence design system
│ │ ├── supabase.js # Supabase auth client
│ │ └── main.jsx
│ ├── .env # Frontend env (Supabase public keys)
│ ├── .env.example
│ └── vite.config.js
│
├── package.json # Root — runs both servers with concurrently
└── README.md
You need free accounts on:
git clone https://github.com/Qaziaaaa/RAG-chatbot.git
cd RAG-chatbot- Create a new project at supabase.com
- Go to SQL Editor in your Supabase dashboard
- Run the migrations in order:
backend/database/schema.sql— creates base tablesbackend/database/schema_v2_migration.sql— adds file upload supportbackend/database/schema_v3_migration.sql— adds per-user data isolation
cd backend
cp .env.example .envEdit backend/.env:
# Groq — get free key at https://console.groq.com/keys
GROQ_API_KEY=your_groq_key_here
LLM_MODEL=llama-3.1-8b-instant
# Supabase — from your project Settings → API
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_ANON_KEY=your_anon_key
SUPABASE_SERVICE_ROLE_KEY=your_service_role_key
# Jina AI — get free key at https://jina.ai/api-dashboard/key-manager
JINA_API_KEY=your_jina_key_herecd frontend
cp .env.example .envEdit frontend/.env:
# Supabase public keys (safe to expose in frontend)
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_ANON_KEY=your_anon_key# From the root folder
npm install # installs concurrently
npm run install:all # installs backend + frontend dependencies# From the root folder — starts both backend and frontend
npm run dev- Frontend: http://localhost:5173
- Backend API: http://localhost:3000
User asks a question
↓
Generate embedding for the question (Jina AI, 768-dim vector)
↓
Search Supabase pgvector for similar document chunks
↓
Retrieve top-5 most relevant chunks
↓
Build a prompt: system instructions + retrieved context + question
↓
Stream the answer from Groq LLM token by token
↓
Display answer + source documents with relevance scores
| Without RAG | With RAG | |
|---|---|---|
| Documents supported | ~10 (token limit) | Thousands |
| Response speed | Slow (huge context) | Fast (small context) |
| API cost | High | Low |
| Accuracy | Lower (noise) | Higher (focused) |
Plain text / Markdown — sentence-aware sliding window (350 chars, 70 char overlap)
Code files — structure-aware splitting at function/class boundaries so each chunk contains a complete, meaningful unit of code
PDFs — text extraction with 3-pass cleanup: normalize whitespace → strip page numbers → collapse horizontal spacing
Each user's documents are stored with their user_id in the database. When logged in, the search RPC filters results to only that user's documents. When logged out, an anonymous UUID from localStorage is used instead.
POST /api/rag/chat/stream
Body: { message, sessionId, userId, mode, documentIds? }
Response: Server-Sent Events stream
data: {"type":"sources", "sources":[...]}
data: {"type":"token", "token":"..."}
data: {"type":"done"}
POST /api/rag/upload
Body: multipart/form-data with field "file"
Response: { jobId, filename, fileSize }
GET /api/rag/upload/:jobId
Response: { status: "pending|processing|done|failed", chunksCreated, documentId }
GET /api/rag/documents
Headers: X-User-Id or Authorization: Bearer <token>
Response: { documents: [...], count }
DELETE /api/rag/documents/:id
Headers: X-User-Id or Authorization: Bearer <token>
GET /api/health
Response: { status, llm, database }
| Variable | Required | Description |
|---|---|---|
GROQ_API_KEY |
Yes | Groq LLM API key |
LLM_MODEL |
No | Model name (default: llama-3.1-8b-instant) |
SUPABASE_URL |
Yes | Your Supabase project URL |
SUPABASE_ANON_KEY |
Yes | Supabase anon/public key |
SUPABASE_SERVICE_ROLE_KEY |
Yes | Supabase service role key (server-side only) |
JINA_API_KEY |
Yes | Jina AI embeddings API key |
| Variable | Required | Description |
|---|---|---|
VITE_SUPABASE_URL |
Yes | Same as backend SUPABASE_URL |
VITE_SUPABASE_ANON_KEY |
Yes | Same as backend SUPABASE_ANON_KEY |
| Service | Free Tier |
|---|---|
| Groq | 14,400 requests/day, 1M tokens/day |
| Supabase | 500MB database, 50MB file storage |
| Jina AI | 1M tokens free (then pay-as-you-go) |
Note: Supabase free projects pause after 7 days of inactivity. To prevent this, set up a free cron job at cron-job.org to ping your
/api/healthendpoint every 3 days.
The app is designed to be deployed with:
Set NODE_ENV=production on the backend. The frontend automatically uses relative API paths in production.
MIT