A full-stack conversational Q&A system that combines persistent PDF document knowledge with ephemeral session-based data using Retrieval-Augmented Generation (RAG).
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ React Frontend │────▶│ TanStack Start │────▶│ PostgreSQL │
│ (Chat UI) │ │ (API Routes) │ │ (Users/Sessions)│
└─────────────────┘ └────────┬─────────┘ └─────────────────┘
│
┌────────────┴────────────┐
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ ChromaDB │ │ OpenAI API │
│ (Vector Store) │ │ (LLM + Embed) │
└─────────────────┘ └─────────────────┘
- Frontend: React + TanStack Router
- Backend: TanStack Start (Nitro server functions)
- Database: PostgreSQL + Drizzle ORM
- Vector DB: ChromaDB
- AI: Vercel AI SDK + OpenAI (gpt-4o-mini)
- Auth: JWT-based authentication
- ✅ User authentication (register/login with JWT)
- ✅ Session management with ephemeral JSON objects
- ✅ PDF upload and text extraction
- ✅ Document chunking and vector embeddings
- ✅ Hybrid RAG combining documents + session objects
- ✅ Real-time streaming responses
- ✅ Source citations in responses
- ✅ Docker containerization
- Node.js 20+
- Docker & Docker Compose
- OpenAI API key
- Clone and install dependencies:
cd pdf-chat
npm install- Copy environment file and configure:
cp .env.example .env.local
# Edit .env.local with your values:
# - OPENAI_API_KEY (required)
# - JWT_SECRET (min 32 chars)
# - DATABASE_URL
# - CHROMA_URL- Start infrastructure (Postgres + ChromaDB):
docker-compose up postgres chromadb -d- Run database migrations:
npm run db:push- Start development server:
npm run dev- Set environment variables:
export OPENAI_API_KEY=your-key
export JWT_SECRET=your-secret-min-32-characters-long
echo "OPENAI_API_KEY=$OPENAI_API_KEY" > .env
echo "JWT_SECRET=$JWT_SECRET" >> .env- Build and run all services:
docker-compose up --buildPOST /api/auth/register- Register new userPOST /api/auth/login- Login and get JWT token
GET /api/sessions- List user sessionsPOST /api/sessions- Create new sessionGET /api/sessions/:id- Get session detailsPUT /api/sessions/:id- Update ephemeral objectsDELETE /api/sessions/:id- Delete session
GET /api/documents- List uploaded documentsPOST /api/documents- Upload PDF (multipart/form-data)DELETE /api/documents/:id- Delete document
POST /api/chat- Send message (SSE streaming response)- Body:
{ sessionId: number, message: string }
- Body:
pdf-chat/
├── src/
│ ├── routes/ # TanStack Router pages & API routes
│ │ ├── api/ # API endpoints
│ │ ├── chat.tsx # Main chat interface
│ │ ├── login.tsx # Login page
│ │ └── register.tsx # Registration page
│ ├── lib/ # Shared utilities
│ │ ├── ai.ts # RAG pipeline & prompts
│ │ ├── api.ts # Frontend API client
│ │ ├── auth.ts # JWT utilities
│ │ ├── chroma.ts # ChromaDB client
│ │ └── pdf.ts # PDF parsing & chunking
│ ├── db/ # Database schema
│ └── components/ # React components
├── docker-compose.yml # Container orchestration
├── Dockerfile # App container
└── drizzle.config.ts # Drizzle ORM config
- Register/Login: Create an account or sign in
- Upload PDFs: Upload documents to build your knowledge base
- Create Session: Start a new chat session
- Add Objects: Define JSON objects in the sidebar for session-specific context
- Chat: Ask questions that combine document knowledge with your objects
[
{ "type": "rectangle", "width": 100, "height": 50, "color": "red" },
{ "type": "circle", "radius": 25, "color": "blue" }
]- "How many objects are in my list?"
- "Do any of my objects violate the size constraints from the uploaded regulations?"
- "Summarize the key points from the uploaded document"
- "Compare the properties of my objects against the specifications"
| Variable | Description | Required |
|---|---|---|
DATABASE_URL |
PostgreSQL connection string | Yes |
OPENAI_API_KEY |
OpenAI API key | Yes |
JWT_SECRET |
Secret for JWT signing (min 32 chars) | Yes |
CHROMA_URL |
ChromaDB server URL | No (default: http://localhost:8000) |
MIT