AskDoc is a full-stack Retrieval-Augmented Generation (RAG) application for asking questions about your PDF documents.
Users sign in with Google, create workspaces, upload PDFs, and chat with an AI assistant that answers using retrieved document chunks and returns source citations.
- Authenticates users with Google OAuth 2.0
- Organizes documents into per-user workspaces
- Ingests PDF files asynchronously (extract, chunk, embed, index)
- Performs semantic retrieval from Qdrant vector search
- Generates grounded answers with Groq-hosted LLMs
- Persists conversation history and cited sources
- NestJS 11, TypeORM, PostgreSQL
- Passport (Google OAuth), JWT cookies (access + refresh rotation)
- Qdrant (
@qdrant/js-client-rest) for vector search - LangChain text splitters + Ollama embeddings
- AI SDK (
@ai-sdk/groq,ai-sdk-ollama)
- React 19 + Vite 7
- TanStack Router + TanStack Query
- Tailwind CSS 4 + Radix UI primitives
- Typed API client generated from OpenAPI (
@hey-api/openapi-ts)
- Docker Compose for local PostgreSQL and Qdrant
- pnpm workspace monorepo (
backend,frontend)
askdoc/
backend/ # NestJS API (auth, workspaces, files, chat)
frontend/ # React SPA
docker-compose.yml
pnpm-workspace.yaml
flowchart LR
U[User Browser\nReact SPA] -->|HTTPS + cookies| API[NestJS API]
API --> DB[(PostgreSQL)]
API --> VDB[(Qdrant)]
API --> OLL[Ollama Embeddings\nnomic-embed-text-v2-moe]
API --> GROQ[Groq LLM\nllama-3.3-70b-versatile]
- Upload PDF -> API stores file metadata -> async processing starts.
- PDF text is extracted and split into chunks.
- Chunks are embedded and indexed in Qdrant with workspace/file metadata.
- Ask question -> question is optionally rewritten to standalone form.
- Query embedding is searched against workspace-scoped vectors.
- Top chunks are passed to the LLM to generate an answer.
- Answer + sources are saved to conversation history and returned to UI.
- Node.js 20+
- pnpm 10+
- Docker + Docker Compose
- Ollama running locally with embedding model pulled:
ollama pull nomic-embed-text-v2-moedocker compose up -dpnpm installCreate env files:
backend/.envfrontend/.env
Use values similar to:
# backend/.env
PORT=3000
FRONTEND_URL=http://localhost:5173
DB_HOST=localhost
DB_PORT=5432
DB_USER=user
DB_PASS=password
DB_NAME=askdoc
QDRANT_URL=http://localhost:6333
JWT_SECRET=<your-jwt-secret>
JWT_EXPIRES_IN=15m
REFRESH_JWT_SECRET=<your-refresh-jwt-secret>
REFRESH_JWT_EXPIRES_IN=7d
GOOGLE_CLIENT_ID=<your-google-client-id>
GOOGLE_CLIENT_SECRET=<your-google-client-secret>
GOOGLE_CALLBACK_URL=http://localhost:3000/auth/google/callback
GROQ_API_KEY=<your-groq-api-key># frontend/.env
VITE_backend_url=http://localhost:3000pnpm dev- Backend:
http://localhost:3000 - Swagger:
http://localhost:3000/api - Frontend:
http://localhost:5173
pnpm dev- run backend + frontend concurrentlypnpm build- build all packages
pnpm --filter ./backend start:devpnpm --filter ./backend testpnpm --filter ./backend lint
pnpm --filter ./frontend devpnpm --filter ./frontend buildpnpm --filter ./frontend lintpnpm --filter ./frontend openapi-ts
GET /auth/google,GET /auth/google/callback,GET /auth/mePOST /auth/refresh,POST /auth/logoutCRUD /workspacesCRUD /workspaces/:workspaceId/filesPOST /workspaces/:workspaceId/chat/queryCRUD /workspaces/:workspaceId/chat/conversations
See full API docs in Swagger at /api.
- TypeORM currently uses
synchronize: true(development convenience). - Move to migrations before production use.
- Do not commit real
.envsecrets.
- Replace polling with SSE/WebSocket updates for file processing status
- Add background job queue (BullMQ) for resilient ingestion retries
- Add workspace sharing / role-based access
- Add test coverage for RAG pipeline and auth/session flows