7.1 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 - Integration tests may use Testcontainers (Azurite, LocalStack)
- All new features require test coverage
Writing Good Test Names
Test names should explain the why (business logic, user scenario, or expected behavior), not the how (implementation details or return values).
Key principles:
- Describe blocks should explain the business goal or rule being tested
- Test names should explain the scenario, context, and reason for the behavior
- Avoid implementation details like "returns X", "should be Y", "calls Z method"
- Focus on user scenarios and business rules
- Make tests readable as documentation - someone unfamiliar with the code should understand what's being tested and why
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)