Kan is an open-source project management tool (Trello alternative) built with:
- Frontend: Next.js, React, TypeScript, Tailwind CSS
- Backend: tRPC, Node.js
- Database: PostgreSQL with Drizzle ORM
- Monorepo: pnpm workspaces with Turbo
- Auth: Better Auth
- Internationalization: Lingui
- Install deps:
pnpm install - Start dev server:
pnpm dev - Create migrations:
cd packages/db && pnpm drizzle-kit generate --name "AddFieldToTable" - Run database migrations:
pnpm db:migrate - Run linter:
pnpm lint - Run type check:
pnpm typecheck - Format code:
pnpm format:fix - Extract i18n strings:
pnpm lingui:extract
apps/web/- Next.js web applicationpackages/api/- tRPC API routerspackages/db/- Database schema, migrations, and repositoriespackages/auth/- Authentication packagepackages/shared/- Shared utilitiespackages/email/- Email templates and sendingpackages/stripe/- Stripe integrationtooling/- Shared tooling configs (ESLint, Prettier, TypeScript)
- Use TypeScript strictly - avoid
anytypes - Prefer explicit types over inference when it improves clarity
- Use
as constfor literal types when appropriate - Follow existing patterns for type definitions
- Files: kebab-case for files (e.g.,
card-repo.ts) - Components: PascalCase for React components
- Functions: camelCase for functions
- Constants: UPPER_SNAKE_CASE for constants
- Types/Interfaces: PascalCase
- Schema: Define schemas in
src/schema/using Drizzle ORM - Migrations: Create migrations with
cd packages/db && pnpm drizzle-kit generate --name "MigrationName", then run withpnpm db:migrate - Repositories: Put database queries in
src/repository/files - Soft Deletes: Use
deletedAttimestamp for soft deletion (not hard deletes) - Index Management: Cards have
indexfields that must be maintained sequentially per list - Activity Logging: Use
card_activitytable to track all card changes
- Routers: Create tRPC routers in
src/routers/ - Procedures: Use
protectedProcedurefor authenticated endpoints,publicProcedurefor public - Validation: Use Zod schemas for input validation
- Error Handling: Use
TRPCErrorwith appropriate error codes - OpenAPI: Add OpenAPI metadata for all endpoints
- Authorization: Always check workspace membership with
assertUserInWorkspace
- Components: React components in
src/components/ - Views: Page-level components in
src/views/ - Hooks: Custom hooks in
src/hooks/ - i18n: Use
ttemplate literal for translations (Lingui) - Styling: Use Tailwind CSS classes
- State Management: Use tRPC React Query hooks for server state
- Modals: Use
useModalhook for modal management - Popups: Use
usePopuphook for toast notifications
- Cards are the main entity in Kan
- Cards belong to Lists, which belong to Boards
- Cards have: title, description, labels, members, checklists, comments, attachments, due dates
- Cards use soft deletion (
deletedAtfield) - Cards have an
indexfield that must be maintained sequentially per list - All card changes are tracked in
card_activitytable
- Every significant card change creates an activity record
- Activity types include: created, updated (various fields), etc.
- Activities are displayed in card activity feeds
- Users belong to Workspaces
- Boards belong to Workspaces
- Workspace members have different permission levels
- Boards can be public or private
- Entities use
deletedAttimestamp for soft deletion - Queries filter with
isNull(table.deletedAt)to exclude deleted items
- Schema:
packages/db/src/schema/*.ts - Repositories:
packages/db/src/repository/*.repo.ts - Migrations:
packages/db/migrations/
- Routers:
packages/api/src/routers/*.ts - Utils:
packages/api/src/utils/ - Types:
packages/api/src/types/
- Components:
apps/web/src/components/ - Views:
apps/web/src/views/ - Pages:
apps/web/src/pages/ - Hooks:
apps/web/src/hooks/ - Utils:
apps/web/src/utils/ - Locales:
apps/web/src/locales/
- Soft Deletes: Always filter with
isNull(table.deletedAt)in queries - Public IDs: Use 12-character public IDs (
publicId) for all user-facing entities - Internal IDs: Never expose internal database IDs (e.g.,
id,cardId,listId) in API responses or URLs - always usepublicIdexternally - Transactions: Use database transactions for multi-step operations
- Index Management: When deleting/moving cards, maintain sequential indices
- Activity Tracking: Create activity records for all significant changes
- Input Validation: Always validate inputs with Zod
- Error Messages: Provide clear, user-friendly error messages
- Optimistic Updates: Use tRPC's
onMutatefor optimistic UI updates - Cache Invalidation: Properly invalidate queries after mutations
- ID Exposure: Never expose internal database IDs (
id,cardId,listId, etc.) in API responses, URLs, or frontend code - always usepublicIdfor external communication
When cards are created, moved, or deleted, their indices must be maintained:
- New cards: Append to end (max index + 1) or insert at position
- Moving cards: Adjust indices of affected cards
- Deleting cards: Decrement indices of cards after deleted one
- Always use transactions for index updates
Create activity records for:
- Card creation
- Card updates (title, description, list, etc.)
- Label/member additions/removals
- Comments
- Checklists and items
- Attachments
- Due dates
Always check:
- User is authenticated
- User has access to workspace
- User has permission for the operation
Use assertUserInWorkspace helper for workspace checks.
- Use TRPCError with appropriate codes (UNAUTHORIZED, NOT_FOUND, etc.)
- Provide user-friendly error messages
- Log errors appropriately
- Show popup notifications for user-facing errors
- Create card in repository with proper index management
- Create
card.createdactivity - Handle label/member relationships if provided
- Return the created card
- Validate user has workspace access
- Update card fields
- Create appropriate activity records
- Invalidate relevant queries
- Always filter by
isNull(cards.deletedAt)in queries - Include related data (labels, members, checklists) via Drizzle relations
- Order by
indexfor proper card ordering
- Database: Update schema in
packages/db/src/schema/ - Migration: Create migration with
cd packages/db && pnpm drizzle-kit generate --name "MigrationName", then run withpnpm db:migrate - Repository: Add repository functions in
packages/db/src/repository/ - API: Add tRPC router procedures in
packages/api/src/routers/ - Frontend: Add UI components in
apps/web/src/ - i18n: Add translations for new strings
- Always create migrations (never modify existing migrations)
- Create migrations with:
cd packages/db && pnpm drizzle-kit generate --name "MigrationName" - Run migrations with:
pnpm db:migrate - Update schema files in
packages/db/src/schema/ - Test migrations on development database first
- Update TypeScript types after schema changes
- Consider index management for card operations
- Use tRPC procedures (not REST)
- Add OpenAPI metadata for documentation
- Validate inputs with Zod
- Check workspace permissions
- Create activity records for significant changes
- Use Tailwind for styling
- Follow existing component patterns
- Use tRPC hooks for data fetching
- Implement optimistic updates where appropriate
- Add proper loading and error states
- Test database operations in transactions that rollback
- Test authorization checks
- Test index management when moving/deleting cards
- Test activity logging
- Test UI interactions
- Run
pnpm lintandpnpm typecheckbefore committing
- Use database indexes appropriately
- Batch operations when possible
- Avoid N+1 queries
- Use transactions for related operations
- Implement optimistic updates in UI
- Always check workspace membership before operations
- Validate all inputs
- Never expose internal database IDs (
id,cardId,listId, etc.) in API responses, URLs, or frontend code - always usepublicIdexternally - Sanitize user input
- All user-facing strings must use
ttemplate literal - Add translations to locale files in
apps/web/src/locales/ - Run
pnpm lingui:extractto update translation files - Update locale files for all languages
- Use workspace dependencies (
workspace:*) for internal packages - Keep dependencies up to date
- Use catalog for shared dependency versions
- Update schema in
packages/db/src/schema/cards.ts - Create migration
- Update repository functions
- Add API endpoints
- Update frontend components
- Add activity tracking if needed
- Add to
activityTypesarray in schema - Create migration to update enum
- Use in activity creation code
- Update activity display components if needed
- Use conventional commits:
feat:,fix:,refactor:,docs:, etc. - Keep commits focused on single changes
- Reference issue numbers when applicable
- Title format:
feat: descriptionorfix: description - Always run
pnpm lintandpnpm typecheckbefore committing - Provide clear description of changes
- Include screenshots for UI changes
- Keep PRs focused on a single feature/fix