Skip to content

Latest commit

 

History

History
398 lines (312 loc) · 11 KB

File metadata and controls

398 lines (312 loc) · 11 KB

Claude Code Reference Guide

This document provides guidance for Claude Code when working on this journal application codebase.

Project Overview

A full-stack journal application with event-driven architecture:

  • Frontend: React + TypeScript (Vite)
  • Backend: NestJS REST API
  • Event Processing: NestJS Microservice (Kafka consumer)
  • Database: PostgreSQL
  • Message Queue: Kafka
  • AI Integration: OpenAI API for journal analysis

Architecture

Monorepo Structure

This is a Yarn workspaces monorepo with the following packages:

/
├── api/                      # NestJS REST API
├── event-consumer/           # NestJS Kafka consumer for analytics
├── web/                      # React frontend (Vite)
├── api-e2e/                 # End-to-end API tests
├── libs/
│   ├── data-access/postgres/ # Shared database DAL
│   └── analysis/            # Shared analysis utilities
├── test-utils/              # Shared test utilities
└── common-types/            # Shared TypeScript types

Key Components

1. API (/api)

  • NestJS REST API with JWT authentication
  • Google OAuth integration
  • Publishes events to Kafka when journals are created
  • Modules: auth, journals, analytics, events

2. Event Consumer (/event-consumer)

  • Kafka consumer listening to journal.events topic
  • Processes JOURNAL_CREATED events
  • Generates AI-powered psychological analysis using OpenAI
  • Implements idempotency with in-memory event tracking

3. Web (/web)

  • React SPA with TypeScript
  • Google OAuth login
  • Journal CRUD operations
  • AI-generated analysis viewing

4. Data Access Layer (/libs/data-access/postgres)

  • Shared PostgreSQL DAL used by API and event-consumer
  • DAL pattern with separate files for each entity
  • TypeScript interfaces for database models

Database Schema

Key Tables

-- Users (OAuth-based, no passwords)
user (userId PK)

-- Journal entries
journal (journalId PK, userId FK, title, body, createdAt)

-- AI-generated analysis
user_analysis (analysisId PK, userId FK, summary, createdAt)

Important Notes

  • User table only has userId field (Google OAuth ID)
  • No email, password, or name fields in user table
  • Cascade deletes configured (delete user → delete journals → delete analyses)

Event-Driven Architecture

Kafka Topics

  • journal.events - Journal lifecycle events

Event Schema

{
  eventId: string;          // Unique event identifier
  eventType: 'JOURNAL_CREATED' | 'JOURNAL_UPDATED' | 'JOURNAL_DELETED';
  timestamp: string;        // ISO 8601
  version: string;          // Event schema version
  payload: {
    userId: string;
    journalId: string;
    title?: string;
    createdAt: string;
  }
}

Event Flow

  1. User creates journal via API
  2. API saves to database
  3. API publishes JOURNAL_CREATED event to Kafka
  4. Event consumer receives event
  5. Consumer fetches journal details
  6. Consumer calls OpenAI API for analysis
  7. Consumer saves analysis to database

Error Handling in Consumer

Important: The consumer implements special error handling for "not found" errors:

// In events.controller.ts
if (error.message.includes('not found')) {
  this.processedEvents.add(event.eventId);
  this.logger.log(`Processed event but journal was not found, ignoring`);
  return; // Don't retry - prevents infinite loop
}
throw error; // Retry for other errors

This prevents infinite retry loops when a journal or user is deleted before the event is processed.

Testing Strategy

Test Types

  1. Unit Tests (Jest)

    • API: yarn test:api
    • Consumer: yarn test:consumer
    • Location: *.spec.ts files next to source code
  2. E2E Tests (Jest + Supertest)

    • API + Kafka integration: yarn test:api:e2e
    • Location: /api-e2e/src/api/*.spec.ts
    • Uses real Kafka consumer to verify event publishing
  3. Frontend Tests (Vitest + Testing Library)

    • Web: yarn test:web
    • Includes Cypress for full E2E: yarn test:web (runs Cypress in CI mode)

Test Conventions

Unit Tests

  • Use NestJS Testing Module for dependency injection
  • Mock external dependencies (database, OpenAI, Kafka)
  • Use custom mock types to avoid any:
    type MockedDatabaseService = {
      journalDAL: {
        getJournalById: jest.MockedFunction<any>;
      };
    };

E2E Tests

  • Use real Kafka consumer to verify event publishing
  • Generate JWT tokens dynamically using generateTestToken()
  • Load secrets from API's .env files via dotenvx
  • Wait for Kafka events: await new Promise(resolve => setTimeout(resolve, 3000))

Database Schema in Tests

  • IMPORTANT: User table only has userId column
  • Correct insert: INSERT INTO "user" ("userId") VALUES ($1)
  • Wrong: INSERT INTO "user" ("userId", "email", "password") ...

JWT Authentication in Tests

E2E tests use JWT tokens without hardcoded secrets:

// In test-auth.ts
export function generateTestToken(userId: string): string {
  const secret = process.env.ACCESS_TOKEN_SECRET;
  return jwt.sign({ sub: userId, userId, email: `${userId}@test.com` }, secret);
}

JWT strategy requires both userId AND email in payload.

Common Commands

Development

yarn start:api          # Start API server
yarn start:consumer     # Start event consumer
yarn start:web          # Start frontend dev server

Testing

yarn test:api           # API unit tests
yarn test:consumer      # Consumer unit tests
yarn test:api:e2e       # E2E tests (API + Kafka)
yarn test:web           # Web tests (Vitest + Cypress)

Code Quality

yarn lint              # Run ESLint
yarn lint:fix          # Fix linting issues
yarn format            # Format with Prettier
yarn format:check      # Check formatting
yarn typecheck         # TypeScript type checking

Database

yarn db:exec           # Execute PostgreSQL script
yarn migrate:oauth     # Run OAuth migration

Environment Variables

Required for API

POSTGRES_HOST=localhost
POSTGRES_PORT=5432
POSTGRES_DB=journal
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
KAFKA_BROKERS=localhost:9093
ACCESS_TOKEN_SECRET=<secret>
JWT_SECRET=<secret>
GOOGLE_CLIENT_ID=<id>
GOOGLE_CLIENT_SECRET=<secret>
OPENAI_API_KEY=<key>

Required for Consumer

POSTGRES_HOST=localhost
POSTGRES_PORT=5432
POSTGRES_DB=journal
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
KAFKA_BROKERS=localhost:9093
OPENAI_API_KEY=<key>

Test Environment

  • E2E tests load API's .env.local and .env files
  • Uses dotenvx for environment variable loading
  • Test setup in /api-e2e/src/support/test-setup.ts

Code Conventions

TypeScript

  • Strict mode enabled
  • No implicit any
  • Interface for database models
  • Type for DTOs and request/response objects

NestJS

  • Module-based architecture
  • Dependency injection for all services
  • DTOs with class-validator decorators
  • Guards for authentication/authorization

File Naming

  • Services: *.service.ts
  • Controllers: *.controller.ts
  • Unit tests: *.spec.ts
  • E2E tests: *.spec.ts (in api-e2e/)
  • DTOs: *.dto.ts

Imports

  • Use absolute imports with workspace aliases:
    • @journal/data-access-postgres
    • @journal/analysis
  • Jest moduleNameMapper configured for test imports

Important Files

Configuration

  • /package.json - Root workspace config
  • /api/src/main.ts - API entry point
  • /event-consumer/src/main.ts - Consumer entry point
  • /api/.env.local - Local environment variables (gitignored)

Testing

  • /api-e2e/src/support/test-auth.ts - JWT token generation for tests
  • /api-e2e/src/support/test-setup.ts - Test environment setup
  • /event-consumer/jest.config.js - Jest config with module mapping

Database

  • /libs/data-access/postgres/src/journal.dal.ts - Journal database access
  • /libs/data-access/postgres/src/user-analysis.dal.ts - Analysis database access

Key Services

  • /api/src/events/event-producer.service.ts - Kafka event publishing
  • /event-consumer/src/events/events.controller.ts - Kafka event consumption
  • /event-consumer/src/analytics/analytics.service.ts - OpenAI integration

Troubleshooting

Common Issues

  1. Tests fail with "column does not exist"

    • Check you're using correct user table schema (only userId column)
    • Don't insert email, password, or name fields
  2. E2E tests get 401 Unauthorized

    • Verify JWT token includes both userId and email
    • Check ACCESS_TOKEN_SECRET is loaded from environment
    • Ensure test-setup.ts loads API's .env files
  3. Kafka events not received in tests

    • Verify tests use API endpoints, not direct database inserts
    • Events only published when journals created via API
    • Add wait time: await new Promise(resolve => setTimeout(resolve, 3000))
  4. Jest can't find module

    • Check jest.config.js has correct moduleNameMapper
    • Verify workspace package names match imports
  5. Consumer retrying forever

    • Check error handling for "not found" errors
    • Should mark event as processed and return (no throw)

Development Workflow

Adding a New Feature

  1. Database changes (if needed)

    • Add migration script in /api/scripts/
    • Update DAL in /libs/data-access/postgres/
    • Update TypeScript interfaces
  2. API changes

    • Add/update DTOs
    • Add/update controller endpoints
    • Add/update service logic
    • Add unit tests
    • Add e2e tests
  3. Event changes (if needed)

    • Update event schema
    • Update event producer
    • Update event consumer
    • Add consumer unit tests
    • Add e2e tests for event flow
  4. Frontend changes

    • Add/update components
    • Add/update API client calls
    • Add tests
  5. Validation

    • Run all tests: yarn test:api && yarn test:consumer && yarn test:api:e2e && yarn test:web
    • Run linting: yarn lint
    • Run type checking: yarn typecheck

Git Workflow

  • Branch naming: feature/description, fix/description
  • Commit messages: Use conventional commits format
  • Always include "Generated with Claude Code" footer in commits

Current Test Coverage

Total: 200/200 tests passing

  • Event-consumer unit tests: 27/27
  • API unit tests: 53/53
  • E2E tests: 12/12
  • Cypress tests: 108/108

Additional Notes

Security

  • JWT tokens for API authentication
  • Google OAuth for user login
  • No passwords stored (OAuth only)
  • Secrets managed via environment variables

Performance

  • Database queries use indexes on foreign keys
  • Kafka ensures reliable event delivery
  • OpenAI API calls are async and don't block request handling

Scaling Considerations

  • Kafka allows multiple consumer instances
  • Consumer uses in-memory event tracking (not persistent across restarts)
  • Could add Redis for persistent idempotency tracking if needed

Next Steps / TODOs

When working on this codebase:

  1. Check this file first for context
  2. Review existing patterns before implementing new features
  3. Maintain test coverage for all new code
  4. Follow existing conventions for file structure and naming
  5. Update this file if you add significant new patterns or conventions