A Next.js application for chatting with uploaded PDF documents using retrieval-augmented generation. Users can upload documents, index their text into a vector database, and ask questions against that knowledge base through a streaming chat interface.
- Authentication with Clerk-protected routes
- PDF upload flow for indexing document content
- Vector search over embedded document chunks stored in Neon Postgres with pgvector
- Streaming chat powered by the AI SDK and OpenAI models
- Simple landing page with navigation to chat and upload flows
- Next.js 16 with the App Router
- Clerk for authentication
- OpenAI via the AI SDK
- Neon serverless Postgres
- Drizzle ORM and drizzle-kit
- Tailwind CSS and Biome
src/app/page.tsx- landing pagesrc/app/chat/page.tsx- chat UIsrc/app/upload/page.tsx- PDF upload UIsrc/app/upload/actions.ts- server action that parses PDFs, chunks text, and stores embeddingssrc/app/api/chat/route.ts- streaming chat endpoint with a knowledge-base search toolsrc/lib/db-config.ts- database connection setupsrc/lib/db-schema.ts- document table and vector index definitionsrc/lib/search.ts- similarity search over stored embeddingssrc/lib/embeddings.ts- OpenAI embedding helperssrc/lib/chunking.ts- text chunking logic
- Node.js 18 or newer
- A Neon Postgres database with the
vectorextension available - Clerk application credentials
- OpenAI API access
Create a .env.local file in the project root with at least the following values:
NEON_DATABASE_URL=your_neon_database_connection_string
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=your_clerk_publishable_key
CLERK_SECRET_KEY=your_clerk_secret_key
OPENAI_API_KEY=your_openai_api_keyInstall dependencies:
npm installRun the development server:
npm run devOpen http://localhost:3000 in your browser.
Apply the Drizzle migrations to your Neon database before using the app:
npx drizzle-kit pushIf your workflow uses generated migration files instead of pushing the schema directly, run the Drizzle command that matches your setup and ensure the documents table exists with the vector index defined in src/lib/db-schema.ts.
npm run dev- start the development servernpm run build- build the production appnpm run start- run the production servernpm run lint- run Biome checksnpm run format- format the codebase with Biome
- A signed-in user uploads a PDF from the upload page.
- The server action extracts text, splits it into chunks, and generates embeddings with OpenAI.
- The chunks and embeddings are stored in Neon Postgres.
- When the user asks a question, the chat route searches the embedded document chunks for relevant context.
- The model answers using the search results and streams the response back to the UI.
- The app uses Clerk middleware to protect non-public routes.
- The chat endpoint is intentionally constrained to keep answers concise and grounded in retrieved context.
- Metadata in the root layout can be updated if you want a project-specific title and description.
