Files
2025-11-29 17:55:11 +08:00

14 KiB

Claude.md Examples

This file provides concrete examples of well-structured claude.md files for different types of directories.

Example 1: Source Code Directory

src/services/

# src/services/

This directory contains the business logic layer that sits between API routes
and the database. Services handle complex operations, business rules, and
orchestrate multiple database operations.

## Purpose

Services provide:
- Business logic implementation
- Transaction management
- Data validation and transformation
- Integration with external APIs
- Caching logic

## Directory Structure

services/ ├── user-service.ts # User management and authentication ├── order-service.ts # Order processing and fulfillment ├── payment-service.ts # Payment processing with Stripe ├── email-service.ts # Email sending via SendGrid └── cache-service.ts # Redis caching utilities

Key Patterns

Service Class Structure

All services follow this pattern:

export class UserService {
  constructor(
    private db: Database,
    private cache: CacheService,
    private logger: Logger
  ) {}

  async getUser(id: string): Promise<User> {
    // 1. Check cache
    // 2. Query database if cache miss
    // 3. Transform data
    // 4. Update cache
    // 5. Return result
  }
}

Error Handling

Services throw domain-specific errors:

  • NotFoundError - Resource doesn't exist
  • ValidationError - Business rule violation
  • ConflictError - Duplicate or conflicting resource
  • ExternalServiceError - Third-party API failure

Transaction Management

Multi-step operations use database transactions:

async transferFunds(from: string, to: string, amount: number) {
  return this.db.transaction(async (trx) => {
    await this.debit(from, amount, trx);
    await this.credit(to, amount, trx);
  });
}

Dependencies

Internal

  • src/database/ - Database client and models
  • src/lib/logger - Logging utilities
  • src/lib/config - Configuration management

External

  • Stripe SDK - Payment processing
  • SendGrid - Email delivery
  • Redis - Caching layer

Testing

Service tests are in tests/services/:

npm run test:services

Each service has:

  • Unit tests with mocked dependencies
  • Integration tests with test database

Notes

  • Services never directly access HTTP request/response objects
  • All external API calls should have timeout and retry logic
  • Cache invalidation happens within the service that modifies data

---

## Example 2: Test Directory

### tests/integration/

```markdown
# tests/integration/

Integration tests that verify multiple components working together, including
database operations, API endpoints, and external service integrations.

## Overview

These tests use a real test database and mock external services. They run
slower than unit tests but provide higher confidence in system behavior.

## Structure

integration/ ├── api/ # Full API endpoint tests ├── services/ # Service layer with real database ├── workflows/ # End-to-end user workflows └── fixtures/ # Shared test data and utilities

Running Tests

# All integration tests (takes ~5 minutes)
npm run test:integration

# Specific test file
npm run test:integration -- api/user-endpoints.test.ts

# Watch mode for development
npm run test:integration:watch

Test Environment

Integration tests use:

  • Database: PostgreSQL test database (auto-created/destroyed)
  • Cache: In-memory Redis
  • External APIs: Mocked using nock
  • Time: Frozen at 2025-01-01T00:00:00Z using timekeeper

Patterns

Test Structure

describe('User Registration Flow', () => {
  beforeAll(async () => {
    await setupTestDatabase();
  });

  afterAll(async () => {
    await teardownTestDatabase();
  });

  beforeEach(async () => {
    await clearTestData();
  });

  it('should create user and send welcome email', async () => {
    // Arrange
    const userData = fixtures.newUser();
    
    // Act
    const response = await api.post('/users', userData);
    
    // Assert
    expect(response.status).toBe(201);
    expect(emailMock).toHaveBeenCalledWith(
      expect.objectContaining({ to: userData.email })
    );
  });
});

Fixtures

Use shared fixtures for consistency:

import { fixtures } from './fixtures';

const user = fixtures.user();  // Random valid user
const admin = fixtures.user({ role: 'admin' });  // Admin user

Database Management

Test Database Setup

Test database is created automatically but can be manually reset:

npm run test:db:reset

Migrations

Integration tests run against the latest schema. If migrations change:

  1. Stop running tests
  2. Run npm run test:db:reset
  3. Restart tests

Common Issues

  • Timeout errors: Increase timeout for slow operations

    it('slow operation', async () => {
      // ...
    }, 10000); // 10 second timeout
    
  • Port conflicts: Tests use port 3001. Ensure nothing else uses it.

  • Database locks: Tests should clean up connections. If stuck:

    npm run test:db:kill-connections
    

Notes

  • Integration tests should NOT make real external API calls
  • Each test should be independent (no shared state)
  • Use transactions for faster test data cleanup when possible

---

## Example 3: Configuration Directory

### config/

```markdown
# config/

Configuration management for all environments (development, staging, production).
Uses environment variables with typed validation and sensible defaults.

## Overview

Configuration is loaded from:
1. Environment variables (highest priority)
2. `.env` file (development only)
3. Default values (fallback)

## Files

- **index.ts**: Main config loader and validation
- **schema.ts**: Zod schemas for type-safe config
- **defaults.ts**: Default values for all settings
- **database.ts**: Database connection configuration
- **redis.ts**: Redis connection configuration
- **stripe.ts**: Stripe API configuration

## Usage

```typescript
import { config } from '@/config';

// Type-safe access
const dbUrl = config.database.url;
const stripeKey = config.stripe.apiKey;

// Environment check
if (config.isProduction) {
  // Production-specific logic
}

Environment Variables

Required (No Defaults)

These MUST be set in production:

DATABASE_URL=postgresql://user:pass@host:5432/db
REDIS_URL=redis://host:6379
STRIPE_API_KEY=sk_live_xxxxx
JWT_SECRET=your-secret-key

Optional (With Defaults)

# Server
PORT=3000
HOST=0.0.0.0
NODE_ENV=development

# Logging
LOG_LEVEL=info
LOG_FORMAT=json

# Features
ENABLE_RATE_LIMITING=true
ENABLE_ANALYTICS=false

Development Setup

Copy example file:

cp .env.example .env

Then edit .env with your local values.

Validation

Config is validated on startup using Zod schemas. Invalid config causes immediate application failure with clear error messages:

❌ Configuration Error:
  - DATABASE_URL is required
  - STRIPE_API_KEY must start with 'sk_'
  - PORT must be a number between 1024 and 65535

Adding New Config

  1. Add schema to schema.ts:
export const newFeatureSchema = z.object({
  enabled: z.boolean().default(false),
  apiKey: z.string().min(1),
});
  1. Add to main config schema in index.ts

  2. Update .env.example with documentation

  3. Add to TypeScript types (auto-generated from schema)

Security

  • Never commit .env files (in .gitignore)
  • Never log sensitive config values
  • Use separate keys per environment
  • Rotate secrets regularly

Notes

  • Config is loaded once at startup and cached
  • Changes require application restart
  • Use feature flags for runtime configuration changes

---

## Example 4: API Routes Directory

### src/api/routes/

```markdown
# src/api/routes/

HTTP route handlers that process requests, validate input, call services,
and format responses. All routes follow RESTful conventions.

## Structure

Routes are organized by resource:

routes/ ├── users.ts # User CRUD operations ├── posts.ts # Post management ├── comments.ts # Comment operations ├── auth.ts # Authentication endpoints └── health.ts # Health check endpoints

Route Pattern

Every route file exports a router:

import { Router } from 'express';
import { authenticate, validate } from '../middleware';
import { userService } from '@/services';
import { createUserSchema, updateUserSchema } from '@/validators';

const router = Router();

// GET /users/:id
router.get('/:id', 
  authenticate,
  asyncHandler(async (req, res) => {
    const user = await userService.getUser(req.params.id);
    res.json(serialize.user(user));
  })
);

// POST /users
router.post('/',
  validate(createUserSchema),
  asyncHandler(async (req, res) => {
    const user = await userService.createUser(req.body);
    res.status(201).json(serialize.user(user));
  })
);

export default router;

Conventions

HTTP Methods

  • GET - Retrieve resources (idempotent)
  • POST - Create new resources
  • PUT - Replace entire resource
  • PATCH - Partial update
  • DELETE - Remove resource

Status Codes

  • 200 - Success with body
  • 201 - Resource created
  • 204 - Success without body
  • 400 - Bad request (validation error)
  • 401 - Unauthorized (not logged in)
  • 403 - Forbidden (logged in but no permission)
  • 404 - Not found
  • 409 - Conflict (duplicate resource)
  • 500 - Server error

URL Structure

  • Plural nouns: /users, /posts
  • Resource IDs: /users/123
  • Nested resources: /posts/123/comments
  • Actions as verbs: /users/123/activate

Middleware Order

  1. Global middleware (in app.ts)

    • Logging
    • CORS
    • Body parsing
  2. Route-specific middleware

    • Authentication
    • Validation
    • Rate limiting
  3. Handler

    • Business logic call
    • Response formatting

Response Format

All JSON responses follow this structure:

// Success
{
  "data": { /* resource or array */ },
  "meta": { /* pagination, etc */ }
}

// Error
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid input",
    "details": [/* field errors */]
  }
}

Adding New Routes

  1. Create route file in this directory
  2. Define handlers with validation
  3. Register in src/api/index.ts:
    app.use('/api/resource', resourceRoutes);
    
  4. Add tests in tests/api/
  5. Update OpenAPI spec in docs/openapi.yaml

Testing Routes

# All route tests
npm run test:routes

# Specific route file
npm run test:routes -- users.test.ts

Dependencies

  • Express Router - Routing
  • express-validator - Input validation
  • Services - Business logic (in src/services/)
  • Serializers - Response formatting (in src/serializers/)

---

## Example 5: Database/Models Directory

### src/models/

```markdown
# src/models/

TypeScript interfaces and Prisma schema definitions that define the data
models used throughout the application.

## Overview

Models are the single source of truth for data structure. They are used by:
- Database operations (Prisma)
- API validation
- Service layer type checking
- Response serialization

## Structure

models/ ├── user.ts # User model and types ├── post.ts # Post model and types ├── comment.ts # Comment model and types └── index.ts # Re-exports all models

The actual database schema is in prisma/schema.prisma.

Model Pattern

// Database model (from Prisma)
export type User = {
  id: string;
  email: string;
  passwordHash: string;
  createdAt: Date;
  updatedAt: Date;
};

// API representation (subset, no sensitive fields)
export type PublicUser = Omit<User, 'passwordHash'>;

// Creation input
export type CreateUserInput = {
  email: string;
  password: string;
};

// Update input (all optional)
export type UpdateUserInput = Partial<{
  email: string;
  password: string;
}>;

Database Schema

The source of truth is prisma/schema.prisma:

model User {
  id           String   @id @default(uuid())
  email        String   @unique
  passwordHash String
  createdAt    DateTime @default(now())
  updatedAt    DateTime @updatedAt
  posts        Post[]
}

Generate TypeScript types after schema changes:

npm run prisma:generate

Relationships

  • User → has many → Post
  • Post → has many → Comment
  • Comment → belongs to → User

Query with relations:

const user = await prisma.user.findUnique({
  where: { id },
  include: { posts: true }
});

Migrations

After modifying schema.prisma:

# Create migration
npm run prisma:migrate:dev

# Apply to production
npm run prisma:migrate:deploy

Type Generation

After database changes, regenerate Prisma types:

npm run prisma:generate

This updates types in node_modules/.prisma/client/ used throughout the app.

Best Practices

  • Keep models focused (single responsibility)
  • Use explicit types for inputs/outputs
  • Never expose passwordHash or other sensitive fields
  • Use unions for status enums:
    export type PostStatus = 'draft' | 'published' | 'archived';
    

Notes

  • Models are pure data structures (no methods)
  • Business logic belongs in services, not models
  • Use Prisma for all database operations (no raw SQL unless necessary)

---

## Template: Generic Directory

For directories that don't fit other categories:

```markdown
# [directory-name]/

[One sentence describing what this directory contains]

## Overview

[2-3 sentences explaining the purpose and scope of this directory]

## Structure

[If complex, show directory tree with brief descriptions]

## Key Files

[Highlight 3-5 most important files and their roles]

## Important Patterns

[Document any conventions, patterns, or standards used here]

## Dependencies

[What this depends on and what depends on this]

## Usage

[How to work with code in this directory, with examples if helpful]

## Notes

[Any gotchas, known issues, or additional context]

Use this template as a starting point and customize based on the specific directory's needs.