6.5 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
Papra is a minimalistic document management and archiving platform built as a monorepo using PNPM workspaces. The project includes a SolidJS frontend, HonoJS backend, CLI tools, and supporting packages. It's open-source and designed for easy self-hosting using Docker, and also offers a cloud-hosted SaaS version.
Architecture
Monorepo Structure
- apps/papra-server: Backend API server (HonoJS + Drizzle ORM + Better Auth)
- apps/papra-client: Frontend application (SolidJS + UnoCSS + Shadcn Solid)
- apps/docs: Documentation site (Astro + Starlight)
- packages/lecture: Text extraction library for documents
- packages/api-sdk: API client SDK
- packages/cli: Command-line interface
- packages/webhooks: Webhook types and utilities
Backend Architecture (apps/papra-server)
The backend follows a modular architecture with feature-based modules:
-
Module pattern: Each feature lives in
src/modules/<feature>/with:*.repository.ts: Database access layer (Drizzle ORM queries)*.usecases.ts: Business logic and orchestration*.routes.ts: HTTP route handlers (Hono)*.services.ts: Service layer for external integrations*.table.ts: Drizzle schema definitions*.types.ts: TypeScript type definitions*.errors.ts: Error definitions
-
Core modules:
app,shared,config,tasks -
Feature modules:
documents,organizations,users,tags,tagging-rules,intake-emails,ingestion-folders,webhooks,api-keys,subscriptions, etc. -
Database: Uses Drizzle ORM with SQLite/Turso (libsql). Schema is in
*.table.tsfiles, migrations insrc/migrations/ -
Authentication: Better Auth library for user auth
-
Task system: Background job processing using Cadence MQ, a custom made queue system (papra-hq/cadence-mq)
-
Document storage: Abstracted storage supporting local filesystem, S3, and Azure Blob
Frontend Architecture (apps/papra-client)
-
SolidJS for reactivity with router (
@solidjs/router) -
Module pattern: Features in
src/modules/<feature>/with:components/: UI componentspages/: Route components*.services.ts: API client calls*.provider.tsx: Context providers*.types.ts: Type definitions
-
Routing: Defined in
src/routes.tsx -
Styling: UnoCSS for atomic CSS with Shadcn Solid components
-
State: TanStack Query for server state, local storage for client state
-
i18n: TypeScript-based translations in
src/locales/*.dictionary.ts
Dependency Injection Pattern
The server uses a dependency injection pattern with @corentinth/chisels/injectArguments to create testable services that accept dependencies as parameters.
Development Commands
Initial Setup
# Install dependencies
pnpm install
# Build all packages (required before running apps)
pnpm build:packages
Backend Development
cd apps/papra-server
# Run database migrations
pnpm migrate:up
# Start development server (localhost:1221)
pnpm dev
# Run tests
pnpm test # All tests
pnpm test:watch # Watch mode
pnpm test:unit # Unit tests only
pnpm test:int # Integration tests only
# Lint and typecheck
pnpm lint
pnpm typecheck
# Database management
pnpm db:studio # Open Drizzle Studio
pnpm migrate:create "migration_name" # Create new migration
Frontend Development
cd apps/papra-client
# Start development server (localhost:3000)
pnpm dev
# Run tests
pnpm test
pnpm test:watch
pnpm test:e2e # Playwright E2E tests
# Lint and typecheck
pnpm lint
pnpm typecheck
# i18n key synchronization
pnpm script:sync-i18n-key-order
Package Development
cd packages/<package-name>
# Build package
pnpm build
pnpm build:watch # Watch mode (or pnpm dev)
# Run tests
pnpm test
pnpm test:watch
Root-level Commands
# Run tests across all packages
pnpm test
pnpm test:watch
# Build all packages
pnpm build:packages
# Version management (changesets)
pnpm changeset # Create changeset
pnpm version # Apply changesets and bump versions
# Docker builds
pnpm docker:build:root
pnpm docker:build:root:amd64
pnpm docker:build:root:arm64
Documentation Development
cd apps/docs
pnpm dev # localhost:4321
Testing Guidelines
- Use Vitest for all testing
- Test files:
*.test.tsfor unit tests,*.int.test.tsfor integration tests - Use business-oriented test names (avoid
it('should return true')) - Integration tests may use Testcontainers (Azurite, LocalStack)
- All new features require test coverage
Code Style
- ESLint config:
@antfu/eslint-config(auto-fix on save recommended) - Conventions:
- Use functional programming where possible
- Prefer clarity and maintainability over performance
- Use meaningful names for variables, functions, and components
- Follow Conventional Commits for commit messages
- Type safety: Strict TypeScript throughout
i18n
- Language files in
apps/papra-client/src/locales/*.dictionary.ts - Reference
en.dictionary.tsfor all keys (English is fallback) - Fully type-safe with TypeScript
- Update
i18n.constants.tswhen adding new languages - Use
pnpm script:sync-i18n-key-orderto sync key order - Branchlet/core: Uses
@branchlet/corefor pluralization and conditional i18n string templates (variant of ICU message format)- Basic interpolation:
'Hello {{ name }}!'with{ name: 'World' } - Conditionals:
'{{ count, =0:no items, =1:one item, many items }}' - Pluralization with variables:
'{{ count, =0:no items, =1:{count} item, {count} items }}' - Range conditions:
'{{ score, [0-50]:bad, [51-75]:good, [76-100]:excellent }}' - See branchlet documentation for more details
- Basic interpolation:
Contributing Flow
- Open an issue before submitting PRs for features/bugs
- Target the
mainbranch (continuously deployed to production) - Keep PRs small and atomic
- Ensure CI is green (linting, type checking, testing, building)
- PRs are squashed on merge
Key Technologies
- Frontend: SolidJS, UnoCSS, Shadcn Solid, TanStack Query, Vite
- Backend: HonoJS, Drizzle ORM, Better Auth, Zod, Cadence MQ
- Database: SQLite/Turso (libsql)
- Testing: Vitest, Playwright, Testcontainers
- Monorepo: PNPM workspaces with catalog for shared dependencies
- Build: esbuild (backend), Vite (frontend), tsdown (packages)