This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
SnapDocs is a modern collaborative document workspace built with:
- Next.js 14 (App Router) for the frontend and API
- PostgreSQL for structured data (users, workspaces, page metadata)
- MongoDB for document storage (page content blocks)
- Prisma as the ORM for PostgreSQL
- shadcn/ui for UI components
- Socket.io for real-time collaboration
- Docker Compose for local development services
# Install dependencies
npm install
# Start Docker services (PostgreSQL, MongoDB, Redis, MinIO)
docker-compose up -d
# Run database migrations
npm run db:migrate
# Generate Prisma client
npm run db:generate
# Start development server with Socket.io
npm run dev
# Open Prisma Studio
npm run db:studio# Type checking
npm run typecheck
# Linting
npm run lint
# Format code
npm run format
# Run tests
npm run test# Run migrations
npm run db:migrate
# Push schema changes (skip migrations)
npm run db:push
# Seed database with sample data
npm run db:seed
# Open Prisma Studio GUI
npm run db:studio# Start all services
docker-compose up -d
# Stop all services
docker-compose down
# Reset everything (including data)
docker-compose down -v
# View logs
docker-compose logs -f [service-name]PostgreSQL (via Prisma) stores:
- User accounts and authentication
- Workspace metadata
- Page hierarchy and metadata
- Permissions and sharing
- Comments and activity logs
- Database schemas
MongoDB stores:
- Page content (blocks array)
- Version history
- Templates
Key Pattern: Each page has metadata in PostgreSQL (pages table) and content in MongoDB (pageContent collection), linked by pageId.
app/- Next.js app router pages and API routescomponents/- React componentseditor/- Block-based editor componentsdatabase/- Database view componentssidebar/- Navigation componentsui/- shadcn/ui base components
lib/- Utilities and helpersdb/- Database connection utilitiesservices/- Business logic servicessocket/- Real-time collaborationcollaboration/- Operational transformation
prisma/- Database schema and migrationstypes/- TypeScript type definitions
Content is stored as an array of blocks:
interface Block {
id: string
type: BlockType // 'paragraph', 'heading1', 'bulletList', etc.
content?: RichText[] | string
properties?: Record<string, any>
children?: Block[]
order: number
}Key files:
components/editor/BlockNoteEditor.tsx- Main editor using BlockNotelib/services/page-content.ts- MongoDB content service
Implemented via Socket.io with operational transformation:
server.js- Custom Socket.io serverlib/socket/client.tsx- Client socket providerlib/collaboration/ot.ts- Conflict resolution
Two-tier sync approach:
- Immediate: Individual block updates via WebSocket
- Periodic: Full document sync every 10 seconds
All API routes follow RESTful conventions:
GET /api/pages- List pagesPOST /api/pages- Create pageGET /api/pages/[id]- Get pagePUT /api/pages/[id]- Update pageDELETE /api/pages/[id]- Delete page
See prisma/schema.prisma for the complete schema. Key models:
User- User accountsWorkspace- Team workspacesPage- Page metadataDatabase- SnapDocs databasesPermission- Access control
Required in .env.local:
DATABASE_URL- PostgreSQL connectionMONGODB_URI- MongoDB connectionNEXTAUTH_SECRET- Auth secretNEXTAUTH_URL- Application URLREDIS_URL- Redis connectionS3_*- MinIO/S3 configuration
The application uses BlockNote as the main editor, which provides:
- Rich text editing with slash commands
- Built-in block types (headings, lists, tables, etc.)
- File uploads and media embedding
- Real-time collaboration support
// Use Prisma for metadata
const page = await prisma.page.create({
data: { title, workspaceId, authorId }
})
// Use MongoDB for content
const db = await getMongoDb()
await db.collection('pageContent').insertOne({
pageId: page.id,
content: { blocks: [] },
version: 1
})// Get metadata from PostgreSQL
const pages = await prisma.page.findMany({
where: { workspaceId }
})
// Get content from MongoDB
const db = await getMongoDb()
const contents = await db.collection('pageContent')
.find({ pageId: { $in: pages.map(p => p.id) } })
.toArray()# Use shadcn/ui CLI to add components
npx shadcn@latest add [component-name]- PostgreSQL for ACID properties on critical metadata
- MongoDB for flexible document storage
- Optimistic UI updates with rollback on conflicts
- Operational transformation for concurrent edits
- Debounced real-time updates (100ms typing, 5s persistence)
- Version history with automatic cleanup (50 versions max)
- Socket.io room-based isolation for scalability
- Workspace-based access control
- Row-level security through membership checks
- Consistent permission validation in API routes
- Socket.io rooms isolate page collaboration
All API routes should:
- Authenticate user with
getCurrentUser() - Validate workspace membership
- Check specific permissions
- Perform operation with proper error handling
- Return consistent response format
- Always use absolute imports with
@/prefix - Components should be client components when they use hooks or browser APIs
- Keep server components for data fetching and static content
- Use Prisma transactions for multi-step database operations
- Always validate workspace membership in API routes
- Use
pageContentServicefor all MongoDB operations - Implement proper error boundaries for production readiness