diff --git a/.cursor/rules/awesome-cursor-rules-mdc/index.mdc b/.cursor/rules/awesome-cursor-rules-mdc/index.mdc new file mode 100644 index 0000000..0519ecb --- /dev/null +++ b/.cursor/rules/awesome-cursor-rules-mdc/index.mdc @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/.cursor/rules/database/config.mdc b/.cursor/rules/database/config.mdc index 0519ecb..9cd95ce 100644 --- a/.cursor/rules/database/config.mdc +++ b/.cursor/rules/database/config.mdc @@ -1 +1,455 @@ - \ No newline at end of file +--- +description: Guidelines for database configuration, management, and optimization +globs: **/*.{sql,prisma,js,ts,py,java,rb,php,go} +version: 1.0.0 +author: Cursor AI +tags: database, configuration, optimization, security, migrations +--- + +# Database Configuration Guidelines + +This document defines guidelines for database configuration, management, and optimization across different environments and database systems. + +## Database Selection Criteria + +When selecting a database system, consider the following criteria: + +1. **Data Model**: Relational, document, key-value, graph, or time-series +2. **Scalability Requirements**: Vertical vs. horizontal scaling needs +3. **Consistency Requirements**: ACID compliance vs. eventual consistency +4. **Performance Requirements**: Read vs. write optimization +5. **Operational Complexity**: Managed service vs. self-hosted +6. **Cost Considerations**: Licensing, hosting, and operational costs +7. **Ecosystem Integration**: Tools, libraries, and community support +8. **Security Features**: Authentication, encryption, and compliance capabilities + +## Primary Database: PostgreSQL + +### Configuration Guidelines + +#### Basic Configuration + +```ini +# postgresql.conf basic settings +listen_addresses = '*' +max_connections = 100 +shared_buffers = 256MB +effective_cache_size = 768MB +maintenance_work_mem = 64MB +checkpoint_completion_target = 0.9 +wal_buffers = 16MB +default_statistics_target = 100 +random_page_cost = 1.1 +effective_io_concurrency = 200 +work_mem = 4MB +min_wal_size = 1GB +max_wal_size = 4GB +``` + +#### Performance Tuning + +Adjust these parameters based on available system resources: + +| Parameter | Small (2GB RAM) | Medium (8GB RAM) | Large (32GB RAM) | +|-----------|----------------|-----------------|------------------| +| `shared_buffers` | 512MB | 2GB | 8GB | +| `effective_cache_size` | 1.5GB | 6GB | 24GB | +| `work_mem` | 4MB | 16MB | 64MB | +| `maintenance_work_mem` | 64MB | 256MB | 1GB | +| `max_connections` | 100 | 200 | 400 | + +#### Security Configuration + +```ini +# Security settings +ssl = on +ssl_cert_file = 'server.crt' +ssl_key_file = 'server.key' +ssl_ciphers = 'HIGH:!aNULL:!MD5' +password_encryption = scram-sha-256 +``` + +### Connection Management + +#### Connection Pooling with PgBouncer + +```ini +# pgbouncer.ini +[databases] +* = host=localhost port=5432 dbname=app + +[pgbouncer] +listen_addr = * +listen_port = 6432 +auth_type = md5 +auth_file = /etc/pgbouncer/userlist.txt +pool_mode = transaction +max_client_conn = 1000 +default_pool_size = 20 +``` + +#### Application Connection Configuration + +```javascript +// Node.js with Prisma example +// .env +DATABASE_URL="postgresql://username:password@localhost:6432/app?schema=public&connection_limit=5" + +// prisma/schema.prisma +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} +``` + +```python +# Python with SQLAlchemy example +from sqlalchemy import create_engine +from sqlalchemy.pool import QueuePool + +engine = create_engine( + "postgresql://username:password@localhost:6432/app", + poolclass=QueuePool, + pool_size=5, + max_overflow=10, + pool_timeout=30, + pool_recycle=1800, +) +``` + +## Environment-Specific Configuration + +### Development Environment + +```ini +# Development PostgreSQL settings +log_statement = 'all' +log_duration = on +debug_print_parse = on +debug_print_rewritten = on +debug_print_plan = on +``` + +```javascript +// Development connection string +DATABASE_URL="postgresql://dev_user:dev_password@localhost:5432/app_dev?schema=public" +``` + +### Testing Environment + +```ini +# Testing PostgreSQL settings +fsync = off +synchronous_commit = off +full_page_writes = off +``` + +```javascript +// Testing connection string +DATABASE_URL="postgresql://test_user:test_password@localhost:5432/app_test?schema=public" +``` + +### Production Environment + +```ini +# Production PostgreSQL settings +log_statement = 'none' +log_duration = off +log_min_duration_statement = 1000 +``` + +```javascript +// Production connection string with connection pooling +DATABASE_URL="postgresql://app_user:strong_password@db.example.com:6432/app_prod?schema=public&connection_limit=10&ssl=true&sslmode=require" +``` + +## Schema Management + +### Migration Strategy + +1. **Version-controlled migrations**: Use tools like Prisma Migrate, Flyway, or Alembic +2. **Forward and backward compatibility**: Ensure migrations can be rolled back +3. **Non-destructive changes**: Prefer additive changes over destructive ones +4. **Staged migrations**: Split complex migrations into smaller, safer steps + +### Example Prisma Migration + +```prisma +// prisma/schema.prisma +model User { + id String @id @default(cuid()) + email String @unique + name String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([email]) +} +``` + +```bash +# Generate and apply migration +npx prisma migrate dev --name add_user_model +``` + +### Database Versioning Best Practices + +1. **One change per migration**: Each migration should make a single logical change +2. **Descriptive names**: Use clear, descriptive names for migration files +3. **Test migrations**: Test migrations in development before applying to production +4. **Document breaking changes**: Clearly document any breaking changes +5. **Include seed data**: Provide seed data for development and testing + +## Indexing Strategy + +### Index Types + +1. **B-tree indexes**: Default index type, good for equality and range queries +2. **Hash indexes**: Optimized for equality comparisons +3. **GIN indexes**: Good for composite values like arrays and JSON +4. **BRIN indexes**: Block Range INdexes for large tables with ordered data +5. **Partial indexes**: Index only a subset of rows that match a condition + +### Indexing Guidelines + +```sql +-- Primary key (automatically indexed) +CREATE TABLE users ( + id SERIAL PRIMARY KEY, + email VARCHAR(255) NOT NULL, + name VARCHAR(255) +); + +-- Index for frequent lookups +CREATE INDEX idx_users_email ON users(email); + +-- Composite index for queries that filter on multiple columns +CREATE INDEX idx_users_name_email ON users(name, email); + +-- Partial index for specific queries +CREATE INDEX idx_active_users ON users(last_login) WHERE status = 'active'; + +-- Expression index +CREATE INDEX idx_users_lower_email ON users(LOWER(email)); +``` + +### Index Maintenance + +```sql +-- Analyze table to update statistics +ANALYZE users; + +-- Rebuild index to remove bloat +REINDEX INDEX idx_users_email; + +-- Monitor index usage +SELECT indexrelname, idx_scan, idx_tup_read, idx_tup_fetch +FROM pg_stat_user_indexes +JOIN pg_stat_user_tables ON idx_rel_id = relid +WHERE schemaname = 'public'; +``` + +## Query Optimization + +### Query Performance Guidelines + +1. **Use prepared statements**: Avoid SQL injection and improve performance +2. **Limit result sets**: Use LIMIT and pagination +3. **Select only needed columns**: Avoid SELECT * +4. **Use appropriate joins**: Choose the right join type (INNER, LEFT, etc.) +5. **Optimize subqueries**: Consider using JOINs instead of subqueries +6. **Use EXISTS instead of IN**: For better performance with large datasets +7. **Batch operations**: Use bulk inserts and updates + +### Example Optimized Queries + +```sql +-- Bad: Selecting all columns, no limit +SELECT * FROM users WHERE status = 'active'; + +-- Good: Selecting only needed columns with limit +SELECT id, email, name FROM users WHERE status = 'active' LIMIT 100; + +-- Bad: Using a subquery +SELECT * FROM orders WHERE user_id IN (SELECT id FROM users WHERE status = 'active'); + +-- Good: Using a join +SELECT o.* FROM orders o +JOIN users u ON o.user_id = u.id +WHERE u.status = 'active'; + +-- Using EXISTS for better performance +SELECT * FROM orders o +WHERE EXISTS (SELECT 1 FROM users u WHERE u.id = o.user_id AND u.status = 'active'); +``` + +## Caching Strategy + +### Application-Level Caching + +```javascript +// Redis caching example with Node.js +const redis = require('redis'); +const client = redis.createClient({ + url: 'redis://localhost:6379' +}); + +async function getUserById(id) { + // Try to get from cache first + const cachedUser = await client.get(`user:${id}`); + if (cachedUser) { + return JSON.parse(cachedUser); + } + + // If not in cache, get from database + const user = await db.user.findUnique({ where: { id } }); + + // Store in cache with expiration + if (user) { + await client.set(`user:${id}`, JSON.stringify(user), { + EX: 3600 // Expire after 1 hour + }); + } + + return user; +} +``` + +### Database Query Caching + +```sql +-- Create a materialized view for expensive queries +CREATE MATERIALIZED VIEW user_stats AS +SELECT + u.id, + u.name, + COUNT(o.id) AS order_count, + SUM(o.total) AS total_spent +FROM users u +LEFT JOIN orders o ON u.id = o.user_id +GROUP BY u.id, u.name; + +-- Create an index on the materialized view +CREATE INDEX idx_user_stats_id ON user_stats(id); + +-- Refresh the materialized view +REFRESH MATERIALIZED VIEW user_stats; +``` + +## Backup and Recovery + +### Backup Strategy + +1. **Regular full backups**: Daily or weekly full database dumps +2. **Continuous WAL archiving**: For point-in-time recovery +3. **Offsite storage**: Store backups in a separate location +4. **Backup verification**: Regularly test restores from backups + +### Backup Commands + +```bash +# PostgreSQL full backup +pg_dump -U username -d dbname -F custom -f backup.dump + +# Incremental backup with WAL archiving +# In postgresql.conf: +# wal_level = replica +# archive_mode = on +# archive_command = 'cp %p /path/to/archive/%f' + +# Restore from backup +pg_restore -U username -d dbname -F custom backup.dump +``` + +## Security Best Practices + +### Authentication and Authorization + +```sql +-- Create a role with limited privileges +CREATE ROLE app_user WITH LOGIN PASSWORD 'strong_password'; + +-- Grant specific privileges +GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_user; +GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO app_user; + +-- Revoke public privileges +REVOKE CREATE ON SCHEMA public FROM PUBLIC; +``` + +### Data Encryption + +1. **Connection encryption**: Use SSL/TLS for all connections +2. **Data-at-rest encryption**: Use filesystem or volume encryption +3. **Sensitive data encryption**: Encrypt sensitive columns + +```sql +-- Enable pgcrypto extension +CREATE EXTENSION pgcrypto; + +-- Encrypt sensitive data +UPDATE users SET + credit_card = pgp_sym_encrypt(credit_card, 'encryption_key'); + +-- Decrypt data when needed +SELECT pgp_sym_decrypt(credit_card::bytea, 'encryption_key') +FROM users WHERE id = 123; +``` + +## Monitoring and Maintenance + +### Key Metrics to Monitor + +1. **Connection count**: Number of active connections +2. **Query performance**: Slow query log and execution times +3. **Index usage**: Unused and inefficient indexes +4. **Cache hit ratio**: Buffer cache effectiveness +5. **Disk usage**: Database and table sizes +6. **Replication lag**: For replicated setups +7. **Lock contention**: Blocked queries and deadlocks + +### Maintenance Tasks + +```sql +-- Update statistics +ANALYZE; + +-- Reclaim space and optimize tables +VACUUM FULL; + +-- Rebuild indexes +REINDEX DATABASE dbname; + +-- Find slow queries +SELECT query, calls, total_time, rows, mean_time +FROM pg_stat_statements +ORDER BY total_time DESC +LIMIT 10; +``` + +## Common Pitfalls + +1. **Connection leaks**: Not properly closing database connections +2. **N+1 query problem**: Making a query for each item in a collection +3. **Over-indexing**: Creating too many indexes, slowing down writes +4. **Under-indexing**: Missing indexes on frequently queried columns +5. **Large transactions**: Long-running transactions causing lock contention +6. **String concatenation in queries**: Leading to SQL injection vulnerabilities +7. **Ignoring database logs**: Missing important error messages and warnings + +## Related Rules + +- [stack/stack-definition](../stack/stack-definition.mdc): Technology stack definition +- [patterns/error-handling](../patterns/error-handling.mdc): Error handling guidelines +- [lessons/technical/database-config](../lessons/technical/database-config.mdc): Database configuration lessons + +## References + +- [PostgreSQL Documentation](https://www.postgresql.org/docs/) +- [Prisma Documentation](https://www.prisma.io/docs) +- [Database Indexing Strategies](https://use-the-index-luke.com/) +- [SQL Performance Explained](https://sql-performance-explained.com/) + +## Changelog + +- 1.0.0: Initial version \ No newline at end of file diff --git a/.cursor/rules/stack/stack-definition.mdc b/.cursor/rules/stack/stack-definition.mdc new file mode 100644 index 0000000..f30b26c --- /dev/null +++ b/.cursor/rules/stack/stack-definition.mdc @@ -0,0 +1,356 @@ +--- +description: Technology stack definition for full-stack application development +globs: **/*.{js,ts,jsx,tsx,py,java,go,rb,php,html,css,scss} +version: 1.0.0 +author: Cursor AI +tags: stack, technology, frontend, backend, database, devops +--- + +# Technology Stack Definition + +This document defines the standard technology stack for full-stack application development, covering frontend, backend, database, and DevOps components. + +## Core Stack Components + +### Frontend + +| Component | Technology | Version | Purpose | +|-----------|------------|---------|---------| +| Framework | React | 18.x | UI component library | +| Meta-framework | Next.js | 14.x | React framework with SSR/SSG | +| State Management | Redux Toolkit | 2.x | Global state management | +| Styling | Tailwind CSS | 3.x | Utility-first CSS framework | +| Component Library | Shadcn UI | latest | Accessible component system | +| Form Handling | React Hook Form | 7.x | Form state management | +| Validation | Zod | 3.x | Schema validation | +| Testing | Vitest + React Testing Library | latest | Unit and component testing | +| E2E Testing | Playwright | latest | End-to-end testing | + +### Backend + +| Component | Technology | Version | Purpose | +|-----------|------------|---------|---------| +| Runtime | Node.js | 20.x LTS | JavaScript runtime | +| Framework | Express.js | 4.x | Web framework | +| API | REST + GraphQL | - | API paradigms | +| GraphQL | Apollo Server | 4.x | GraphQL implementation | +| Authentication | JWT + OAuth 2.0 | - | Authentication protocols | +| Validation | Zod | 3.x | Schema validation | +| Testing | Jest | 29.x | Unit and integration testing | + +### Database + +| Component | Technology | Version | Purpose | +|-----------|------------|---------|---------| +| Primary Database | PostgreSQL | 16.x | Relational database | +| ORM | Prisma | 5.x | Database toolkit | +| Migrations | Prisma Migrate | - | Database schema migrations | +| Caching | Redis | 7.x | In-memory data store | +| Search | Elasticsearch | 8.x | Full-text search engine | + +### DevOps + +| Component | Technology | Version | Purpose | +|-----------|------------|---------|---------| +| Containerization | Docker | latest | Application containerization | +| Orchestration | Kubernetes | latest | Container orchestration | +| CI/CD | GitHub Actions | - | Continuous integration/deployment | +| Infrastructure as Code | Terraform | latest | Infrastructure provisioning | +| Monitoring | Prometheus + Grafana | latest | Metrics and visualization | +| Logging | ELK Stack | latest | Log aggregation and analysis | + +## Technology Selection Criteria + +When selecting technologies for the stack, the following criteria were considered: + +1. **Maturity**: Preference for stable, well-established technologies +2. **Community Support**: Active community and regular updates +3. **Performance**: Optimal performance characteristics for the use case +4. **Developer Experience**: Ease of use and productivity +5. **Scalability**: Ability to scale with application growth +6. **Security**: Built-in security features and regular security updates +7. **Integration**: Seamless integration with other stack components +8. **Documentation**: Comprehensive and up-to-date documentation + +## Implementation Guidelines + +### Frontend Implementation + +#### Project Structure + +``` +frontend/ +├── public/ # Static assets +├── src/ +│ ├── app/ # Next.js App Router +│ │ ├── ui/ # Basic UI components +│ │ └── features/ # Feature-specific components +│ ├── hooks/ # Custom React hooks +│ ├── lib/ # Utility functions and libraries +│ ├── store/ # Redux store configuration +│ │ ├── slices/ # Redux slices +│ │ └── index.ts # Store configuration +│ ├── styles/ # Global styles +│ └── types/ # TypeScript type definitions +├── .eslintrc.js # ESLint configuration +├── .prettierrc # Prettier configuration +├── jest.config.js # Jest configuration +├── next.config.js # Next.js configuration +├── package.json # Dependencies and scripts +├── tailwind.config.js # Tailwind CSS configuration +└── tsconfig.json # TypeScript configuration +``` + +#### Coding Standards + +- Use TypeScript for all frontend code +- Follow functional component patterns with hooks +- Implement proper error boundaries +- Use React Query for data fetching and caching +- Implement responsive design using Tailwind's utilities +- Follow accessibility best practices (WCAG 2.1 AA) + +### Backend Implementation + +#### Project Structure + +``` +backend/ +├── src/ +│ ├── api/ # API routes and controllers +│ │ ├── controllers/ # Request handlers +│ │ ├── middlewares/ # Express middlewares +│ │ ├── routes/ # Route definitions +│ │ └── validators/ # Request validation +│ ├── config/ # Application configuration +│ ├── db/ # Database connection and models +│ ├── graphql/ # GraphQL schema and resolvers +│ ├── services/ # Business logic +│ ├── types/ # TypeScript type definitions +│ ├── utils/ # Utility functions +│ └── app.ts # Express application setup +├── .eslintrc.js # ESLint configuration +├── .prettierrc # Prettier configuration +├── jest.config.js # Jest configuration +├── nodemon.json # Nodemon configuration +├── package.json # Dependencies and scripts +├── tsconfig.json # TypeScript configuration +└── prisma/ # Prisma schema and migrations + ├── schema.prisma # Database schema + └── migrations/ # Migration files +``` + +#### Coding Standards + +- Use TypeScript for all backend code +- Implement proper error handling and logging +- Follow RESTful API design principles +- Use dependency injection for services +- Implement proper authentication and authorization +- Follow security best practices (OWASP Top 10) + +### Database Implementation + +#### Schema Design Principles + +- Use appropriate data types for columns +- Implement proper indexing for frequently queried fields +- Use foreign key constraints for referential integrity +- Implement soft deletes where appropriate +- Use enums for fixed sets of values +- Follow naming conventions for tables and columns + +#### Example Prisma Schema + +```prisma +// Example Prisma schema +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" +} + +model User { + id String @id @default(cuid()) + email String @unique + name String? + password String + role Role @default(USER) + posts Post[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([email]) +} + +model Post { + id String @id @default(cuid()) + title String + content String? + published Boolean @default(false) + author User @relation(fields: [authorId], references: [id]) + authorId String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([authorId]) +} + +enum Role { + USER + ADMIN +} +``` + +### DevOps Implementation + +#### CI/CD Pipeline + +```yaml +# Example GitHub Actions workflow +name: CI/CD Pipeline + +on: + push: + branches: [main, develop] + pull_request: + branches: [main, develop] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up Node.js + uses: actions/setup-node@v3 + with: + node-version: '20' + - name: Install dependencies + run: npm ci + - name: Run linting + run: npm run lint + - name: Run tests + run: npm test + + build: + needs: test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up Node.js + uses: actions/setup-node@v3 + with: + node-version: '20' + - name: Install dependencies + run: npm ci + - name: Build application + run: npm run build + - name: Upload build artifacts + uses: actions/upload-artifact@v3 + with: + name: build + path: build/ + + deploy: + needs: build + if: github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Download build artifacts + uses: actions/download-artifact@v3 + with: + name: build + path: build/ + - name: Deploy to production + run: | + # Deployment steps +``` + +#### Docker Configuration + +```dockerfile +# Example Dockerfile for Node.js application +FROM node:20-alpine AS builder + +WORKDIR /app + +COPY package*.json ./ +RUN npm ci + +COPY . . +RUN npm run build + +FROM node:20-alpine AS runner + +WORKDIR /app + +ENV NODE_ENV production + +COPY --from=builder /app/package*.json ./ +RUN npm ci --production + +COPY --from=builder /app/dist ./dist + +USER node + +CMD ["node", "dist/app.js"] +``` + +## Alternative Stacks + +### Alternative Frontend Stack + +| Component | Technology | Considerations | +|-----------|------------|----------------| +| Framework | Vue.js | Better for teams familiar with Vue | +| Meta-framework | Nuxt.js | Vue equivalent of Next.js | +| State Management | Pinia | Simpler than Redux | +| Styling | Sass | More powerful but less utility-focused | + +### Alternative Backend Stack + +| Component | Technology | Considerations | +|-----------|------------|----------------| +| Runtime | Deno | Better security, but fewer packages | +| Framework | NestJS | More structured, Angular-like | +| Language | Python (FastAPI) | Better for data science integration | +| Language | Go | Better performance, but steeper learning curve | + +### Alternative Database Stack + +| Component | Technology | Considerations | +|-----------|------------|----------------| +| Database | MongoDB | Better for document-oriented data | +| Database | MySQL | More widely used than PostgreSQL | +| ORM | TypeORM | More traditional ORM approach | +| ORM | Sequelize | Mature ORM with good migration support | + +## Stack Evolution + +The technology stack should be reviewed and updated regularly: + +- Quarterly review of minor version updates +- Semi-annual review of major version updates +- Annual review of stack components +- Deprecation plan for technologies being phased out + +## Related Rules + +- [frontend/react-nextjs](../tech/frontend/react-nextjs.mdc): Specific guidelines for React and Next.js development +- [typescript/style](../typescript/style.mdc): TypeScript coding standards +- [database/config](../database/config.mdc): Database configuration guidelines + +## References + +- [React Documentation](https://react.dev/) +- [Next.js Documentation](https://nextjs.org/docs) +- [Prisma Documentation](https://www.prisma.io/docs) +- [Node.js Best Practices](https://github.com/goldbergyoni/nodebestpractices) + +## Changelog + +- 1.0.0: Initial version \ No newline at end of file diff --git a/.cursor/rules/tech/frontend/react-nextjs.mdc b/.cursor/rules/tech/frontend/react-nextjs.mdc new file mode 100644 index 0000000..6eabdc0 --- /dev/null +++ b/.cursor/rules/tech/frontend/react-nextjs.mdc @@ -0,0 +1,1004 @@ +--- +description: Guidelines for React and Next.js development +globs: **/*.{js,jsx,ts,tsx} +version: 1.0.0 +author: Cursor AI +tags: react, nextjs, frontend, javascript, typescript +--- + +# React and Next.js Development Guidelines + +This document defines guidelines for developing applications using React and Next.js, covering best practices, architecture patterns, performance optimization, and common pitfalls. + +## Project Structure + +### Next.js App Router Structure + +``` +src/ +├── app/ # App Router +│ ├── (auth)/ # Route group for authentication +│ │ ├── login/ # Login route +│ │ │ ├── page.tsx # Login page component +│ │ │ └── actions.ts # Server actions for login +│ │ └── register/ # Register route +│ ├── api/ # API routes +│ │ └── [...] # API route handlers +│ ├── dashboard/ # Dashboard route +│ │ ├── layout.tsx # Dashboard layout +│ │ ├── page.tsx # Dashboard page component +│ │ └── loading.tsx # Loading UI +│ ├── layout.tsx # Root layout +│ ├── page.tsx # Home page +│ └── globals.css # Global styles +├── components/ # Shared components +│ ├── ui/ # UI components +│ │ ├── button.tsx # Button component +│ │ └── [...] # Other UI components +│ └── features/ # Feature-specific components +│ ├── auth/ # Authentication components +│ └── [...] # Other feature components +├── lib/ # Utility functions and libraries +│ ├── utils.ts # General utilities +│ └── [...] # Other utility files +├── hooks/ # Custom React hooks +│ ├── use-auth.ts # Authentication hook +│ └── [...] # Other hooks +├── types/ # TypeScript type definitions +│ └── index.ts # Type exports +└── styles/ # Component styles + └── [...] # Style files +``` + +### Next.js Pages Router Structure (Legacy) + +``` +src/ +├── pages/ # Pages Router +│ ├── _app.tsx # Custom App component +│ ├── _document.tsx # Custom Document component +│ ├── index.tsx # Home page +│ ├── dashboard.tsx # Dashboard page +│ └── api/ # API routes +│ └── [...] # API route handlers +├── components/ # Shared components +├── lib/ # Utility functions and libraries +├── hooks/ # Custom React hooks +├── types/ # TypeScript type definitions +└── styles/ # Component styles +``` + +## Component Architecture + +### Component Organization + +1. **Atomic Design Methodology**: + - **Atoms**: Basic UI elements (Button, Input, Text) + - **Molecules**: Simple component combinations (SearchBar, FormField) + - **Organisms**: Complex UI sections (Header, UserProfile) + - **Templates**: Page layouts without specific content + - **Pages**: Complete pages with actual content + +2. **Feature-Based Organization**: + - Group components by feature or domain + - Include all related components, hooks, and utilities + +### Component Best Practices + +#### Functional Components + +```tsx +// Good: Functional component with TypeScript +import { useState, useEffect } from 'react'; + +interface UserProfileProps { + userId: string; + showStats?: boolean; +} + +export function UserProfile({ userId, showStats = false }: UserProfileProps) { + const [user, setUser] = useState(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function fetchUser() { + try { + setLoading(true); + const data = await fetchUserData(userId); + setUser(data); + } catch (error) { + console.error('Failed to fetch user:', error); + } finally { + setLoading(false); + } + } + + fetchUser(); + }, [userId]); + + if (loading) return ; + if (!user) return ; + + return ( +
+

{user.name}

+

{user.email}

+ {showStats && } +
+ ); +} +``` + +#### Component Composition + +```tsx +// Good: Component composition +function Dashboard() { + return ( + +
+ + +
+ + + + + + + +
+ ); +} +``` + +#### Custom Hooks + +```tsx +// Good: Custom hook for form handling +function useForm(initialValues: T) { + const [values, setValues] = useState(initialValues); + const [errors, setErrors] = useState>>({}); + const [touched, setTouched] = useState>>({}); + + const handleChange = (e: React.ChangeEvent) => { + const { name, value } = e.target; + setValues({ ...values, [name]: value }); + }; + + const handleBlur = (e: React.FocusEvent) => { + const { name } = e.target; + setTouched({ ...touched, [name]: true }); + }; + + const reset = () => { + setValues(initialValues); + setErrors({}); + setTouched({}); + }; + + return { + values, + errors, + touched, + handleChange, + handleBlur, + reset, + setValues, + setErrors, + }; +} + +// Usage +function LoginForm() { + const { values, handleChange, handleBlur, errors } = useForm({ + email: '', + password: '', + }); + + // Form implementation +} +``` + +## State Management + +### Local State + +```tsx +// Use useState for component-level state +function Counter() { + const [count, setCount] = useState(0); + + return ( +
+

Count: {count}

+ +
+ ); +} +``` + +### Context API + +```tsx +// Create a context +import { createContext, useContext, useState, ReactNode } from 'react'; + +interface AuthContextType { + user: User | null; + login: (email: string, password: string) => Promise; + logout: () => void; +} + +const AuthContext = createContext(undefined); + +// Provider component +export function AuthProvider({ children }: { children: ReactNode }) { + const [user, setUser] = useState(null); + + const login = async (email: string, password: string) => { + // Login implementation + const userData = await loginUser(email, password); + setUser(userData); + }; + + const logout = () => { + // Logout implementation + setUser(null); + }; + + return ( + + {children} + + ); +} + +// Custom hook to use the context +export function useAuth() { + const context = useContext(AuthContext); + if (context === undefined) { + throw new Error('useAuth must be used within an AuthProvider'); + } + return context; +} +``` + +### External State Management + +```tsx +// Redux Toolkit example +import { createSlice, PayloadAction } from '@reduxjs/toolkit'; + +interface CounterState { + value: number; +} + +const initialState: CounterState = { + value: 0, +}; + +export const counterSlice = createSlice({ + name: 'counter', + initialState, + reducers: { + increment: (state) => { + state.value += 1; + }, + decrement: (state) => { + state.value -= 1; + }, + incrementByAmount: (state, action: PayloadAction) => { + state.value += action.payload; + }, + }, +}); + +export const { increment, decrement, incrementByAmount } = counterSlice.actions; +export default counterSlice.reducer; +``` + +## Data Fetching + +### Client-Side Data Fetching + +```tsx +// Using React Query +import { useQuery } from '@tanstack/react-query'; + +function UserList() { + const { data, isLoading, error } = useQuery({ + queryKey: ['users'], + queryFn: fetchUsers, + }); + + if (isLoading) return ; + if (error) return ; + + return ( +
    + {data.map((user) => ( +
  • {user.name}
  • + ))} +
+ ); +} +``` + +### Server-Side Data Fetching (Next.js) + +```tsx +// App Router: Server Component +export default async function UserPage({ params }: { params: { id: string } }) { + const user = await fetchUser(params.id); + + return ( +
+

{user.name}

+

{user.email}

+
+ ); +} + +// Pages Router: getServerSideProps +export async function getServerSideProps(context) { + const { id } = context.params; + const user = await fetchUser(id); + + return { + props: { + user, + }, + }; +} +``` + +### API Routes (Next.js) + +```tsx +// App Router: Route Handler +import { NextResponse } from 'next/server'; + +export async function GET(request: Request) { + const users = await fetchUsers(); + return NextResponse.json(users); +} + +export async function POST(request: Request) { + const data = await request.json(); + const newUser = await createUser(data); + return NextResponse.json(newUser, { status: 201 }); +} + +// Pages Router: API Route +export default async function handler(req, res) { + if (req.method === 'GET') { + const users = await fetchUsers(); + res.status(200).json(users); + } else if (req.method === 'POST') { + const newUser = await createUser(req.body); + res.status(201).json(newUser); + } else { + res.setHeader('Allow', ['GET', 'POST']); + res.status(405).end(`Method ${req.method} Not Allowed`); + } +} +``` + +## Styling Approaches + +### CSS Modules + +```tsx +// styles/Button.module.css +.button { + padding: 8px 16px; + border-radius: 4px; + font-weight: 600; +} + +.primary { + background-color: #0070f3; + color: white; +} + +// Button.tsx +import styles from './Button.module.css'; + +interface ButtonProps { + variant?: 'primary' | 'secondary'; + children: React.ReactNode; + onClick?: () => void; +} + +export function Button({ + variant = 'primary', + children, + onClick +}: ButtonProps) { + return ( + + ); +} +``` + +### Tailwind CSS + +```tsx +// Button.tsx with Tailwind +interface ButtonProps { + variant?: 'primary' | 'secondary'; + children: React.ReactNode; + onClick?: () => void; +} + +export function Button({ + variant = 'primary', + children, + onClick +}: ButtonProps) { + const baseClasses = 'px-4 py-2 rounded font-semibold'; + const variantClasses = { + primary: 'bg-blue-600 text-white hover:bg-blue-700', + secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300', + }; + + return ( + + ); +} +``` + +### CSS-in-JS (Styled Components) + +```tsx +// Button.tsx with styled-components +import styled from 'styled-components'; + +const StyledButton = styled.button<{ variant: 'primary' | 'secondary' }>` + padding: 8px 16px; + border-radius: 4px; + font-weight: 600; + + ${props => props.variant === 'primary' && ` + background-color: #0070f3; + color: white; + + &:hover { + background-color: #0060df; + } + `} + + ${props => props.variant === 'secondary' && ` + background-color: #f1f1f1; + color: #333; + + &:hover { + background-color: #e1e1e1; + } + `} +`; + +interface ButtonProps { + variant?: 'primary' | 'secondary'; + children: React.ReactNode; + onClick?: () => void; +} + +export function Button({ + variant = 'primary', + children, + onClick +}: ButtonProps) { + return ( + + {children} + + ); +} +``` + +## Performance Optimization + +### Code Splitting + +```tsx +// Dynamic import with Next.js +import dynamic from 'next/dynamic'; + +const DynamicChart = dynamic(() => import('../components/Chart'), { + loading: () =>

Loading chart...

, + ssr: false, // Disable server-side rendering if needed +}); + +function Dashboard() { + return ( +
+

Dashboard

+ +
+ ); +} +``` + +### Memoization + +```tsx +// Using React.memo +import { memo } from 'react'; + +interface UserListItemProps { + user: User; + onSelect: (userId: string) => void; +} + +const UserListItem = memo(function UserListItem({ + user, + onSelect +}: UserListItemProps) { + return ( +
  • onSelect(user.id)}> + {user.name} +
    +

    {user.name}

    +

    {user.email}

    +
    +
  • + ); +}); + +// Using useMemo and useCallback +import { useMemo, useCallback } from 'react'; + +function UserList({ users, onUserSelect }) { + // Memoize expensive calculations + const sortedUsers = useMemo(() => { + return [...users].sort((a, b) => a.name.localeCompare(b.name)); + }, [users]); + + // Memoize callback functions + const handleSelect = useCallback((userId: string) => { + onUserSelect(userId); + }, [onUserSelect]); + + return ( +
      + {sortedUsers.map((user) => ( + + ))} +
    + ); +} +``` + +### Image Optimization (Next.js) + +```tsx +// Using Next.js Image component +import Image from 'next/image'; + +function ProfileCard({ user }) { + return ( +
    + {user.name} +

    {user.name}

    +
    + ); +} +``` + +## Testing + +### Component Testing + +```tsx +// Using React Testing Library +import { render, screen, fireEvent } from '@testing-library/react'; +import { Button } from './Button'; + +describe('Button', () => { + it('renders correctly', () => { + render(); + expect(screen.getByText('Click me')).toBeInTheDocument(); + }); + + it('calls onClick when clicked', () => { + const handleClick = jest.fn(); + render(); + fireEvent.click(screen.getByText('Click me')); + expect(handleClick).toHaveBeenCalledTimes(1); + }); + + it('applies the correct class for primary variant', () => { + render(); + const button = screen.getByText('Primary'); + expect(button).toHaveClass('primary'); + }); +}); +``` + +### Integration Testing + +```tsx +// Testing a form submission +import { render, screen, fireEvent, waitFor } from '@testing-library/react'; +import { LoginForm } from './LoginForm'; + +// Mock the API call +jest.mock('../api/auth', () => ({ + login: jest.fn(() => Promise.resolve({ success: true })), +})); + +describe('LoginForm', () => { + it('submits the form with user credentials', async () => { + const mockLogin = require('../api/auth').login; + const onSuccess = jest.fn(); + + render(); + + // Fill out the form + fireEvent.change(screen.getByLabelText(/email/i), { + target: { value: 'user@example.com' }, + }); + + fireEvent.change(screen.getByLabelText(/password/i), { + target: { value: 'password123' }, + }); + + // Submit the form + fireEvent.click(screen.getByRole('button', { name: /log in/i })); + + // Wait for the API call to complete + await waitFor(() => { + expect(mockLogin).toHaveBeenCalledWith({ + email: 'user@example.com', + password: 'password123', + }); + expect(onSuccess).toHaveBeenCalled(); + }); + }); +}); +``` + +## Accessibility + +### Keyboard Navigation + +```tsx +// Ensuring keyboard accessibility +function Dropdown({ options, onSelect }) { + const [isOpen, setIsOpen] = useState(false); + const [selectedOption, setSelectedOption] = useState(null); + const dropdownRef = useRef(null); + + const handleToggle = () => setIsOpen(!isOpen); + + const handleSelect = (option) => { + setSelectedOption(option); + onSelect(option); + setIsOpen(false); + }; + + const handleKeyDown = (e) => { + if (e.key === 'Escape') { + setIsOpen(false); + } + }; + + return ( +
    + + + {isOpen && ( +
      + {options.map((option) => ( +
    • handleSelect(option)} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + handleSelect(option); + } + }} + > + {option.label} +
    • + ))} +
    + )} +
    + ); +} +``` + +### ARIA Attributes + +```tsx +// Using ARIA attributes for accessibility +function Tabs({ tabs }) { + const [activeTab, setActiveTab] = useState(0); + + return ( +
    +
    + {tabs.map((tab, index) => ( + + ))} +
    + + {tabs.map((tab, index) => ( + + ))} +
    + ); +} +``` + +## SEO (Next.js) + +### Metadata + +```tsx +// App Router: Metadata +import { Metadata } from 'next'; + +export const metadata: Metadata = { + title: 'Product Page', + description: 'View our latest products', + openGraph: { + title: 'Product Page', + description: 'View our latest products', + images: [ + { + url: 'https://example.com/og-image.jpg', + width: 1200, + height: 630, + alt: 'Product showcase', + }, + ], + }, +}; + +export default function ProductPage() { + return ( + // Page content + ); +} + +// Pages Router: Head component +import Head from 'next/head'; + +function ProductPage({ product }) { + return ( + <> + + {product.name} | My Store + + + + + + + {/* Page content */} + + ); +} +``` + +## Error Handling + +### Error Boundaries + +```tsx +// Custom error boundary component +import { Component, ErrorInfo, ReactNode } from 'react'; + +interface ErrorBoundaryProps { + fallback?: ReactNode; + children: ReactNode; + onError?: (error: Error, errorInfo: ErrorInfo) => void; +} + +interface ErrorBoundaryState { + hasError: boolean; +} + +class ErrorBoundary extends Component { + constructor(props: ErrorBoundaryProps) { + super(props); + this.state = { hasError: false }; + } + + static getDerivedStateFromError(_: Error): ErrorBoundaryState { + return { hasError: true }; + } + + componentDidCatch(error: Error, errorInfo: ErrorInfo) { + console.error('Error caught by ErrorBoundary:', error, errorInfo); + this.props.onError?.(error, errorInfo); + } + + render() { + if (this.state.hasError) { + return this.props.fallback ||
    Something went wrong.
    ; + } + + return this.props.children; + } +} + +// Usage +function App() { + return ( + } + onError={(error, errorInfo) => { + // Log to error reporting service + logError(error, errorInfo); + }} + > + + + ); +} +``` + +### Next.js Error Handling + +```tsx +// App Router: Error component +'use client'; + +import { useEffect } from 'react'; + +interface ErrorComponentProps { + error: Error; + reset: () => void; +} + +export default function Error({ error, reset }: ErrorComponentProps) { + useEffect(() => { + // Log the error to an error reporting service + console.error(error); + }, [error]); + + return ( +
    +

    Something went wrong!

    +

    {error.message}

    + +
    + ); +} + +// Pages Router: Custom error page +// pages/_error.js +function Error({ statusCode }) { + return ( +
    +

    Error {statusCode}

    +

    + {statusCode + ? `An error ${statusCode} occurred on server` + : 'An error occurred on client'} +

    +
    + ); +} + +Error.getInitialProps = ({ res, err }) => { + const statusCode = res ? res.statusCode : err ? err.statusCode : 404; + return { statusCode }; +}; + +export default Error; +``` + +## Common Pitfalls + +1. **Prop Drilling**: Passing props through multiple component levels + - Solution: Use Context API or state management libraries + +2. **Unnecessary Re-renders**: Components re-rendering when they don't need to + - Solution: Use memoization (React.memo, useMemo, useCallback) + +3. **Memory Leaks**: Not cleaning up effects and subscriptions + - Solution: Return cleanup functions from useEffect + +4. **Excessive API Calls**: Making too many API calls + - Solution: Use caching and debouncing + +5. **Large Bundle Sizes**: Including too many dependencies + - Solution: Use code splitting and tree shaking + +6. **Poor TypeScript Usage**: Not leveraging TypeScript effectively + - Solution: Use proper typing and avoid `any` + +7. **Accessibility Issues**: Not making components accessible + - Solution: Follow WCAG guidelines and use ARIA attributes + +## Best Practices + +1. **Keep Components Small**: Focus on single responsibility +2. **Use TypeScript**: For better type safety and developer experience +3. **Follow Naming Conventions**: Use descriptive names for components and functions +4. **Optimize Rendering**: Minimize unnecessary re-renders +5. **Handle Errors Gracefully**: Use error boundaries and proper error handling +6. **Write Tests**: Cover components with unit and integration tests +7. **Document Components**: Use JSDoc or Storybook for documentation +8. **Follow Accessibility Guidelines**: Make your app usable by everyone +9. **Use ESLint and Prettier**: For consistent code style +10. **Keep Dependencies Updated**: Regularly update dependencies for security and features + +## Related Rules + +- [typescript/style](../../typescript/style.mdc): TypeScript coding standards +- [stack/stack-definition](../../stack/stack-definition.mdc): Technology stack definition +- [patterns/error-handling](../../patterns/error-handling.mdc): Error handling guidelines + +## References + +- [React Documentation](https://react.dev/) +- [Next.js Documentation](https://nextjs.org/docs) +- [React TypeScript Cheatsheet](https://react-typescript-cheatsheet.netlify.app/) +- [React Patterns](https://reactpatterns.com/) + +## Changelog + +- 1.0.0: Initial version \ No newline at end of file diff --git a/.cursor/rules/tech/nextjs/app-router.mdc b/.cursor/rules/tech/nextjs/app-router.mdc new file mode 100644 index 0000000..b6902c8 --- /dev/null +++ b/.cursor/rules/tech/nextjs/app-router.mdc @@ -0,0 +1,912 @@ +--- +description: Guidelines for using Next.js App Router +globs: **/app/**/*.{js,jsx,ts,tsx} +version: 1.0.0 +author: Cursor AI +tags: nextjs, app-router, react, server-components, routing +--- + +# Next.js App Router Guidelines + +This document defines guidelines for using the Next.js App Router, covering best practices, architecture patterns, and common pitfalls. + +## App Router Fundamentals + +### Directory Structure + +``` +app/ +├── (auth)/ # Route group (doesn't affect URL) +│ ├── login/ # /login route +│ │ ├── page.tsx # UI for /login +│ │ └── actions.ts # Server actions for login +│ └── register/ # /register route +│ └── page.tsx # UI for /register +├── api/ # API routes +│ └── [apiRoute]/ # Dynamic API route +│ └── route.ts # API route handler +├── dashboard/ # /dashboard route +│ ├── [id]/ # Dynamic route segment +│ │ └── page.tsx # UI for /dashboard/[id] +│ ├── layout.tsx # Layout for dashboard routes +│ ├── loading.tsx # Loading UI for dashboard +│ ├── error.tsx # Error UI for dashboard +│ └── page.tsx # UI for /dashboard +├── favicon.ico # Favicon +├── globals.css # Global styles +├── layout.tsx # Root layout +├── not-found.tsx # 404 page +└── page.tsx # UI for / (home) +``` + +### File Conventions + +| File | Purpose | +|------|---------| +| `page.tsx` | UI for a route | +| `layout.tsx` | Shared UI for a segment and its children | +| `loading.tsx` | Loading UI for a segment | +| `error.tsx` | Error UI for a segment | +| `not-found.tsx` | Not found UI for a segment | +| `route.ts` | API endpoint | +| `middleware.ts` | Middleware for routes | +| `default.tsx` | Fallback UI for parallel routes | +| `template.tsx` | Re-rendered layout for a segment | +| `global-error.tsx` | Global error UI | + +## Server and Client Components + +### Server Components (Default) + +Server Components are the default in the App Router and render on the server. + +```tsx +// app/users/page.tsx +// This is a Server Component by default +export default async function UsersPage() { + // This data fetching happens on the server + const users = await fetchUsers(); + + return ( +
    +

    Users

    +
      + {users.map(user => ( +
    • {user.name}
    • + ))} +
    +
    + ); +} +``` + +### Client Components + +Use the `"use client"` directive at the top of the file to mark a component as a Client Component. + +```tsx +// app/components/counter.tsx +"use client" + +import { useState } from 'react'; + +export default function Counter() { + const [count, setCount] = useState(0); + + return ( +
    +

    Count: {count}

    + +
    + ); +} +``` + +### Component Composition Pattern + +Compose Server and Client Components effectively: + +```tsx +// app/dashboard/page.tsx (Server Component) +import UserProfile from './user-profile'; // Server Component +import Counter from '../components/counter'; // Client Component +import ServerDataProvider from './server-data-provider'; // Server Component + +export default async function DashboardPage() { + const userData = await fetchUserData(); + + return ( +
    +

    Dashboard

    + + + {/* Client Component nested inside Server Component */} + +
    + ); +} +``` + +## Data Fetching + +### Server Component Data Fetching + +```tsx +// app/products/[id]/page.tsx +export default async function ProductPage({ params }: { params: { id: string } }) { + // This fetch happens on the server + const product = await fetch(`https://api.example.com/products/${params.id}`, { + // Optional: cache control + next: { + revalidate: 60, // Revalidate every 60 seconds + } + }).then(res => res.json()); + + return ( +
    +

    {product.name}

    +

    {product.description}

    +

    ${product.price}

    +
    + ); +} +``` + +### Parallel Data Fetching + +```tsx +// app/dashboard/page.tsx +export default async function DashboardPage() { + // Fetch data in parallel + const [userData, statsData, notificationsData] = await Promise.all([ + fetchUserData(), + fetchUserStats(), + fetchNotifications(), + ]); + + return ( +
    +

    Dashboard

    + + + +
    + ); +} +``` + +### Static and Dynamic Rendering + +```tsx +// Static rendering (default) +export default async function BlogPage() { + const posts = await fetchBlogPosts(); + + return ( +
    +

    Blog

    + +
    + ); +} + +// Dynamic rendering +export const dynamic = 'force-dynamic'; +export default async function DashboardPage() { + const data = await fetchDashboardData(); + + return ( +
    +

    Dashboard

    + +
    + ); +} +``` + +## Routing and Navigation + +### Link Component + +```tsx +// app/components/navigation.tsx +"use client" + +import Link from 'next/link'; + +export default function Navigation() { + return ( + + ); +} +``` + +### Programmatic Navigation + +```tsx +// app/components/login-form.tsx +"use client" + +import { useState } from 'react'; +import { useRouter } from 'next/navigation'; + +export default function LoginForm() { + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const router = useRouter(); + + const handleSubmit = async (e) => { + e.preventDefault(); + const success = await loginUser(email, password); + + if (success) { + router.push('/dashboard'); + // Or refresh the current route + // router.refresh(); + } + }; + + return ( +
    + {/* Form fields */} +
    + ); +} +``` + +### Dynamic Routes + +```tsx +// app/blog/[slug]/page.tsx +export default async function BlogPost({ params }: { params: { slug: string } }) { + const post = await fetchPost(params.slug); + + return ( +
    +

    {post.title}

    +
    +
    + ); +} + +// Generate static paths at build time +export async function generateStaticParams() { + const posts = await fetchPosts(); + + return posts.map((post) => ({ + slug: post.slug, + })); +} +``` + +## Layouts and Templates + +### Root Layout + +```tsx +// app/layout.tsx +import { Inter } from 'next/font/google'; +import './globals.css'; + +const inter = Inter({ subsets: ['latin'] }); + +export const metadata = { + title: { + template: '%s | My App', + default: 'My App', + }, + description: 'My awesome Next.js application', +}; + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + +
    + +
    +
    {children}
    +
    {/* Footer */}
    + + + ); +} +``` + +### Nested Layouts + +```tsx +// app/dashboard/layout.tsx +export default function DashboardLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( +
    + +
    + {children} +
    +
    + ); +} +``` + +### Templates + +Templates are similar to layouts but create a new instance on navigation. + +```tsx +// app/blog/template.tsx +export default function BlogTemplate({ + children, +}: { + children: React.ReactNode; +}) { + return ( +
    + {/* This will be re-mounted on navigation */} + {children} +
    + ); +} +``` + +## Loading and Error States + +### Loading UI + +```tsx +// app/dashboard/loading.tsx +export default function DashboardLoading() { + return ( +
    +
    +

    Loading dashboard...

    +
    + ); +} +``` + +### Error Handling + +```tsx +// app/dashboard/error.tsx +"use client" + +import { useEffect } from 'react'; + +export default function DashboardError({ + error, + reset, +}: { + error: Error & { digest?: string }; + reset: () => void; +}) { + useEffect(() => { + // Log the error to an error reporting service + console.error(error); + }, [error]); + + return ( +
    +

    Something went wrong!

    +

    {error.message}

    + +
    + ); +} +``` + +### Not Found Page + +```tsx +// app/not-found.tsx +import Link from 'next/link'; + +export default function NotFound() { + return ( +
    +

    404 - Page Not Found

    +

    The page you are looking for does not exist.

    + Return to Home +
    + ); +} + +// Custom not found for a specific route +// app/blog/[slug]/not-found.tsx +export default function BlogPostNotFound() { + return ( +
    +

    Blog Post Not Found

    +

    The blog post you are looking for does not exist.

    + Return to Blog +
    + ); +} +``` + +## Server Actions + +### Form Actions + +```tsx +// app/contact/actions.ts +"use server" + +import { z } from 'zod'; +import { redirect } from 'next/navigation'; + +const ContactFormSchema = z.object({ + name: z.string().min(1, "Name is required"), + email: z.string().email("Invalid email address"), + message: z.string().min(10, "Message must be at least 10 characters"), +}); + +export async function submitContactForm(formData: FormData) { + // Validate form data + const validatedFields = ContactFormSchema.safeParse({ + name: formData.get('name'), + email: formData.get('email'), + message: formData.get('message'), + }); + + // Return early if validation fails + if (!validatedFields.success) { + return { + success: false, + errors: validatedFields.error.flatten().fieldErrors, + }; + } + + // Process the form data + try { + await saveContactMessage(validatedFields.data); + + // Redirect on success + redirect('/contact/thank-you'); + } catch (error) { + return { + success: false, + errors: { + form: 'Failed to submit the form. Please try again.', + }, + }; + } +} +``` + +```tsx +// app/contact/page.tsx +import { submitContactForm } from './actions'; + +export default function ContactPage() { + return ( +
    +

    Contact Us

    +
    +
    + + +
    +
    + + +
    +
    + + +
    + +
    +
    + ); +} +``` + +### Client Component with Server Actions + +```tsx +// app/components/contact-form.tsx +"use client" + +import { useRef, useState } from 'react'; +import { submitContactForm } from '../contact/actions'; + +export default function ContactForm() { + const formRef = useRef(null); + const [errors, setErrors] = useState(null); + const [isSubmitting, setIsSubmitting] = useState(false); + + async function handleSubmit(formData: FormData) { + setIsSubmitting(true); + + const result = await submitContactForm(formData); + + setIsSubmitting(false); + + if (!result?.success) { + setErrors(result?.errors || { form: 'Something went wrong' }); + return; + } + + // Reset form on success + formRef.current?.reset(); + setErrors(null); + } + + return ( +
    + {/* Form fields */} + {errors?.form &&
    {errors.form}
    } + +
    + ); +} +``` + +## Metadata and SEO + +### Static Metadata + +```tsx +// app/layout.tsx +import type { Metadata } from 'next'; + +export const metadata: Metadata = { + title: { + template: '%s | My App', + default: 'My App', + }, + description: 'My awesome Next.js application', + keywords: ['Next.js', 'React', 'JavaScript'], + authors: [{ name: 'John Doe', url: 'https://johndoe.com' }], + openGraph: { + type: 'website', + locale: 'en_US', + url: 'https://myapp.com', + siteName: 'My App', + title: 'My App', + description: 'My awesome Next.js application', + images: [ + { + url: 'https://myapp.com/og-image.jpg', + width: 1200, + height: 630, + alt: 'My App', + }, + ], + }, + twitter: { + card: 'summary_large_image', + title: 'My App', + description: 'My awesome Next.js application', + creator: '@johndoe', + images: ['https://myapp.com/twitter-image.jpg'], + }, + robots: { + index: true, + follow: true, + }, +}; +``` + +### Dynamic Metadata + +```tsx +// app/blog/[slug]/page.tsx +import type { Metadata } from 'next'; + +// Generate metadata based on route params +export async function generateMetadata({ + params +}: { + params: { slug: string } +}): Promise { + const post = await fetchPost(params.slug); + + if (!post) { + return { + title: 'Post Not Found', + }; + } + + return { + title: post.title, + description: post.excerpt, + openGraph: { + title: post.title, + description: post.excerpt, + images: [ + { + url: post.featuredImage, + width: 1200, + height: 630, + alt: post.title, + }, + ], + }, + }; +} + +export default function BlogPost({ params }: { params: { slug: string } }) { + // Component implementation +} +``` + +## Route Handlers (API Routes) + +### Basic Route Handler + +```tsx +// app/api/hello/route.ts +import { NextResponse } from 'next/server'; + +export async function GET() { + return NextResponse.json({ message: 'Hello, world!' }); +} +``` + +### Dynamic Route Handler + +```tsx +// app/api/users/[id]/route.ts +import { NextResponse } from 'next/server'; + +export async function GET( + request: Request, + { params }: { params: { id: string } } +) { + const id = params.id; + const user = await fetchUser(id); + + if (!user) { + return NextResponse.json( + { error: 'User not found' }, + { status: 404 } + ); + } + + return NextResponse.json(user); +} + +export async function PUT( + request: Request, + { params }: { params: { id: string } } +) { + const id = params.id; + const data = await request.json(); + + try { + const updatedUser = await updateUser(id, data); + return NextResponse.json(updatedUser); + } catch (error) { + return NextResponse.json( + { error: 'Failed to update user' }, + { status: 500 } + ); + } +} + +export async function DELETE( + request: Request, + { params }: { params: { id: string } } +) { + const id = params.id; + + try { + await deleteUser(id); + return NextResponse.json({ success: true }, { status: 200 }); + } catch (error) { + return NextResponse.json( + { error: 'Failed to delete user' }, + { status: 500 } + ); + } +} +``` + +### Route Handler with Middleware + +```tsx +// app/api/protected/route.ts +import { NextResponse } from 'next/server'; +import { getServerSession } from 'next-auth'; +import { authOptions } from '@/lib/auth'; + +export async function GET(request: Request) { + const session = await getServerSession(authOptions); + + if (!session) { + return NextResponse.json( + { error: 'Unauthorized' }, + { status: 401 } + ); + } + + // Process authenticated request + return NextResponse.json({ + message: 'Protected data', + user: session.user + }); +} +``` + +## Middleware + +```tsx +// middleware.ts +import { NextResponse } from 'next/server'; +import type { NextRequest } from 'next/server'; + +export function middleware(request: NextRequest) { + const { pathname } = request.nextUrl; + + // Redirect if path starts with /dashboard + if (pathname.startsWith('/dashboard')) { + // Check for authentication token + const token = request.cookies.get('auth-token')?.value; + + if (!token) { + // Redirect to login page if not authenticated + return NextResponse.redirect(new URL('/login', request.url)); + } + } + + // Continue with the request + return NextResponse.next(); +} + +// Configure paths that should run middleware +export const config = { + matcher: ['/dashboard/:path*', '/api/protected/:path*'], +}; +``` + +## Performance Optimization + +### Component Level Optimization + +```tsx +// app/components/heavy-component.tsx +"use client" + +import { useState, useCallback, useMemo } from 'react'; + +export default function HeavyComponent({ data }) { + const [filter, setFilter] = useState(''); + + // Memoize expensive calculations + const filteredData = useMemo(() => { + return data.filter(item => + item.name.toLowerCase().includes(filter.toLowerCase()) + ); + }, [data, filter]); + + // Memoize event handlers + const handleFilterChange = useCallback((e) => { + setFilter(e.target.value); + }, []); + + return ( +
    + +
      + {filteredData.map(item => ( +
    • {item.name}
    • + ))} +
    +
    + ); +} +``` + +### Route Segment Config Options + +```tsx +// app/blog/page.tsx + +// Static rendering (default) +export const dynamic = 'force-static'; + +// Dynamic rendering +// export const dynamic = 'force-dynamic'; + +// Revalidate data every 60 seconds +export const revalidate = 60; + +// Prefer loading fallbacks over streaming +export const fetchCache = 'force-cache'; + +// Disable suspense for this route +export const suspense = false; + +export default function BlogPage() { + // Component implementation +} +``` + +## Common Pitfalls + +1. **Mixing Server and Client Code**: Using client-only features in Server Components + - Solution: Properly separate client and server code + +2. **Overusing Client Components**: Making everything a Client Component + - Solution: Start with Server Components and add Client Components as needed + +3. **Inefficient Data Fetching**: Fetching the same data multiple times + - Solution: Use React Cache or fetch data at the highest common parent + +4. **Not Using Loading UI**: Causing poor user experience during loading + - Solution: Implement loading.tsx files for each route segment + +5. **Ignoring Error Handling**: Not providing proper error UI + - Solution: Implement error.tsx files for each route segment + +6. **Misusing Route Handlers**: Using route handlers for data that could be fetched directly + - Solution: Fetch data directly in Server Components when possible + +7. **Forgetting Metadata**: Not providing proper SEO metadata + - Solution: Implement metadata for all pages + +## Best Practices + +1. **Prefer Server Components**: Start with Server Components and add Client Components only when needed +2. **Colocate Related Files**: Keep related files (page, layout, loading, error) together +3. **Optimize Component Boundaries**: Carefully choose the boundary between Server and Client Components +4. **Use Parallel Routes for Complex Layouts**: Implement parallel routes for complex UI patterns +5. **Implement Proper Loading States**: Add loading.tsx files for better UX +6. **Handle Errors Gracefully**: Add error.tsx files for error handling +7. **Optimize Metadata**: Provide proper metadata for SEO +8. **Use Server Actions for Forms**: Implement server actions for form handling +9. **Leverage Static Generation**: Use static generation when possible for better performance +10. **Follow the Next.js App Router Mental Model**: Understand how the App Router works + +## Related Rules + +- [tech/frontend/react-nextjs](../../frontend/react-nextjs.mdc): Guidelines for React and Next.js development +- [patterns/error-handling](../../../patterns/error-handling.mdc): Error handling guidelines +- [stack/stack-definition](../../../stack/stack-definition.mdc): Technology stack definition + +## References + +- [Next.js App Router Documentation](https://nextjs.org/docs/app) +- [React Server Components](https://nextjs.org/docs/app/building-your-application/rendering/server-components) +- [Next.js Data Fetching](https://nextjs.org/docs/app/building-your-application/data-fetching) +- [Next.js Routing](https://nextjs.org/docs/app/building-your-application/routing) + +## Changelog + +- 1.0.0: Initial version \ No newline at end of file diff --git a/.cursor/rules/workflows/code-review.mdc b/.cursor/rules/workflows/code-review.mdc new file mode 100644 index 0000000..3ef4db8 --- /dev/null +++ b/.cursor/rules/workflows/code-review.mdc @@ -0,0 +1,438 @@ +--- +description: Guidelines for effective code review practices +globs: **/*.{js,ts,jsx,tsx,py,java,kt,go,rb,php,cs,c,cpp,h,hpp,css,scss,html,md} +version: 1.0.0 +author: Cursor AI +tags: workflow, code-review, quality, collaboration +--- + +# Code Review Guidelines + +This document defines guidelines for conducting effective code reviews, ensuring code quality, knowledge sharing, and team collaboration. + +## Core Principles + +### 1. Purpose of Code Reviews + +Code reviews serve multiple important purposes: + +- **Quality Assurance**: Identify bugs, logic errors, and edge cases +- **Knowledge Sharing**: Spread knowledge across the team +- **Consistency**: Ensure adherence to coding standards and best practices +- **Mentorship**: Provide learning opportunities for both reviewer and author +- **Collective Ownership**: Foster shared responsibility for the codebase + +### 2. Review Mindset + +Approach code reviews with the right mindset: + +- **Be Respectful**: Focus on the code, not the person +- **Be Constructive**: Suggest improvements rather than just pointing out problems +- **Be Collaborative**: Work together to find the best solution +- **Be Thorough**: Take the time to understand the code and its context +- **Be Timely**: Provide feedback promptly to maintain development velocity + +## Code Review Process + +### 1. Pre-Review Checklist (Author) + +Before submitting code for review, ensure: + +- [ ] Code is complete and implements the required functionality +- [ ] All tests pass (unit, integration, end-to-end) +- [ ] Code follows project style guidelines and best practices +- [ ] Self-review has been performed to catch obvious issues +- [ ] PR/commit description clearly explains the changes and rationale + +```markdown +# Example Pull Request Template + +## Description +Brief description of the changes and the problem they solve. + +## Type of Change +- [ ] Bug fix +- [ ] New feature +- [ ] Breaking change +- [ ] Documentation update + +## How Has This Been Tested? +Describe the tests that you ran to verify your changes. + +## Checklist +- [ ] My code follows the style guidelines of this project +- [ ] I have performed a self-review of my own code +- [ ] I have commented my code, particularly in hard-to-understand areas +- [ ] I have made corresponding changes to the documentation +- [ ] My changes generate no new warnings +- [ ] I have added tests that prove my fix is effective or that my feature works +- [ ] New and existing unit tests pass locally with my changes +``` + +### 2. Review Process + +#### Size and Scope + +- Keep reviews small (ideally < 400 lines of code) +- Focus on a single logical change per review +- Break large changes into smaller, sequential PRs + +#### Review Steps + +1. **Understand the Context**: Read the PR description and related issues +2. **Check Functionality**: Verify the code does what it claims to do +3. **Review Tests**: Ensure proper test coverage and test quality +4. **Examine Implementation**: Look for bugs, edge cases, and design issues +5. **Check Style**: Verify adherence to coding standards +6. **Provide Feedback**: Comment on issues and suggest improvements +7. **Approve or Request Changes**: Based on the review findings + +### 3. Post-Review Process + +- **Author Addresses Feedback**: Make requested changes or discuss alternatives +- **Reviewer Re-reviews**: Verify changes address the feedback +- **Final Approval**: Approve the PR when all issues are resolved +- **Merge**: Merge the changes into the target branch + +## What to Look For + +### 1. Functionality + +- Does the code correctly implement the requirements? +- Are all edge cases handled appropriately? +- Is there proper error handling? +- Are there any performance concerns? + +### 2. Code Quality + +- Is the code readable and maintainable? +- Are functions and classes appropriately sized and focused? +- Is there any duplicated code that could be refactored? +- Are variable and function names clear and descriptive? + +```javascript +// Poor naming +function process(d) { + let r = 0; + for (let i = 0; i < d.length; i++) { + r += d[i].v; + } + return r; +} + +// Better naming +function calculateTotalValue(items) { + let totalValue = 0; + for (let i = 0; i < items.length; i++) { + totalValue += items[i].value; + } + return totalValue; +} +``` + +### 3. Architecture and Design + +- Does the code follow SOLID principles? +- Is the code properly modularized? +- Are dependencies managed appropriately? +- Does the design align with the project's architecture? + +### 4. Security + +- Are there any potential security vulnerabilities? +- Is sensitive data handled securely? +- Are inputs properly validated and sanitized? +- Are authentication and authorization implemented correctly? + +```javascript +// Security issue: SQL injection vulnerability +function getUserData(userId) { + const query = `SELECT * FROM users WHERE id = ${userId}`; + return db.execute(query); +} + +// Fixed: Using parameterized query +function getUserData(userId) { + const query = `SELECT * FROM users WHERE id = ?`; + return db.execute(query, [userId]); +} +``` + +### 5. Testing + +- Is there adequate test coverage? +- Do tests verify both happy paths and edge cases? +- Are tests clear and maintainable? +- Are mocks and test doubles used appropriately? + +```javascript +// Example of a good test +test('should calculate total price with tax', () => { + // Arrange + const items = [ + { name: 'Item 1', price: 10 }, + { name: 'Item 2', price: 20 } + ]; + const taxRate = 0.1; + + // Act + const result = calculateTotalWithTax(items, taxRate); + + // Assert + expect(result).toBe(33); // (10 + 20) * 1.1 +}); +``` + +### 6. Documentation + +- Is the code adequately documented? +- Are complex algorithms or business rules explained? +- Are public APIs documented? +- Are comments up-to-date and helpful? + +```javascript +/** + * Calculates the total price of items with tax applied. + * + * @param {Array} items - Array of items with price property + * @param {number} taxRate - Tax rate as a decimal (e.g., 0.1 for 10%) + * @returns {number} Total price including tax + * @throws {Error} If taxRate is negative + */ +function calculateTotalWithTax(items, taxRate) { + if (taxRate < 0) { + throw new Error('Tax rate cannot be negative'); + } + + const subtotal = items.reduce((sum, item) => sum + item.price, 0); + return subtotal * (1 + taxRate); +} +``` + +## Providing Effective Feedback + +### 1. Be Specific and Actionable + +- Clearly identify the issue +- Explain why it's a problem +- Suggest a specific improvement + +```markdown +// Vague feedback +"This code could be better." + +// Specific and actionable feedback +"The nested if statements on lines 45-60 make the logic hard to follow. Consider extracting this into a separate function or using early returns to reduce nesting." +``` + +### 2. Prioritize Feedback + +Categorize feedback by importance: + +- **Must Fix**: Bugs, security issues, or critical design flaws +- **Should Improve**: Code quality issues that should be addressed +- **Consider**: Suggestions that would be nice but aren't critical +- **Nitpick**: Minor style or preference issues + +### 3. Ask Questions + +- Use questions to understand the author's intent +- Encourage the author to think about alternatives +- Avoid assuming you know better + +```markdown +"I notice you're using a for-loop here instead of Array.map(). Was there a specific reason for this choice? Using map() might make the code more concise and functional." +``` + +### 4. Provide Context and Resources + +- Link to relevant documentation or examples +- Explain the reasoning behind your suggestions +- Share knowledge that might not be obvious + +### 5. Acknowledge Good Code + +- Point out well-written code and clever solutions +- Balance criticism with positive feedback +- Recognize improvements from previous reviews + +## Language-Specific Review Guidelines + +### JavaScript/TypeScript + +- Check for proper type definitions and usage +- Look for potential null/undefined issues +- Verify proper async/await and Promise handling +- Check for memory leaks (especially event listeners) +- Ensure proper error handling in async code + +```typescript +// Issues to watch for +function processData(data: any) { // 'any' type should be avoided + if (data) { // Doesn't check for empty objects/arrays + return data.items.map(item => item.value); // Potential null reference + } +} + +// Better approach +function processData(data: { items?: Array<{ value: number }> }): number[] { + if (!data?.items?.length) { + return []; + } + + return data.items.map(item => item.value); +} +``` + +### Python + +- Check for proper use of Python idioms +- Verify exception handling +- Look for proper use of list comprehensions vs. loops +- Check for potential performance issues +- Verify proper use of typing + +```python +# Issues to watch for +def process_items(items): + result = [] + for item in items: + try: + result.append(item.value * 2) + except: # Bare except clause + pass # Silently ignoring errors + +# Better approach +from typing import List, Optional + +def process_items(items: List[Optional[object]]) -> List[int]: + result = [] + for item in items: + try: + if hasattr(item, 'value') and isinstance(item.value, (int, float)): + result.append(item.value * 2) + except AttributeError: + # Log the error or handle specifically + logger.warning(f"Item {item} has no 'value' attribute") + return result +``` + +### Java/Kotlin + +- Check for proper null handling +- Verify resource cleanup (try-with-resources) +- Look for proper exception handling +- Check for thread safety issues +- Verify proper use of collections + +## Code Review Tools and Automation + +### 1. Static Analysis Tools + +Integrate static analysis tools to automate basic checks: + +- Linters (ESLint, Pylint, etc.) +- Type checkers (TypeScript, MyPy, etc.) +- Style checkers (Prettier, Black, etc.) +- Security scanners (SonarQube, Snyk, etc.) + +### 2. Automated Testing + +Ensure automated tests run on all PRs: + +- Unit tests +- Integration tests +- End-to-end tests +- Performance tests (when relevant) + +### 3. Code Review Platforms + +Use features provided by code review platforms: + +- Inline comments +- Suggested changes +- Review templates +- Automated checks + +## Remote and Asynchronous Reviews + +### 1. Written Communication + +- Be extra clear and detailed in written feedback +- Provide context that might be obvious in person +- Use code snippets and examples to illustrate points + +### 2. Video Reviews + +- Consider recording video walkthroughs for complex changes +- Use screen sharing for interactive reviews when needed +- Record pair programming sessions for knowledge sharing + +### 3. Time Zone Considerations + +- Be mindful of time zone differences +- Set clear expectations for review turnaround times +- Use asynchronous tools effectively + +## Code Review Metrics + +Track these metrics to improve the review process: + +- **Review turnaround time**: Time from PR submission to first review +- **Review resolution time**: Time from PR submission to merge +- **Review size**: Lines of code per review +- **Defect detection rate**: Bugs found during review vs. after merge +- **Review participation**: Distribution of reviews across the team + +## Common Pitfalls + +### 1. Nitpicking + +- Focus on substantive issues, not just style preferences +- Don't let perfect be the enemy of good +- Use automated tools for style enforcement + +### 2. Rubber Stamping + +- Avoid approving code without thorough review +- Take the time to understand the changes +- Don't rush reviews due to time pressure + +### 3. Scope Creep + +- Keep reviews focused on the current changes +- Open separate issues for unrelated improvements +- Don't block merges for unrelated issues + +### 4. Delayed Reviews + +- Prioritize reviews to maintain team velocity +- Set aside dedicated time for reviews +- Communicate delays if you can't review promptly + +## Best Practices + +1. **Review Often**: Frequent, small reviews are better than infrequent, large ones +2. **Automate What You Can**: Use tools to catch basic issues +3. **Face-to-Face for Complex Issues**: Use video calls for discussing complex feedback +4. **Separate Style from Substance**: Focus on logic and design in manual reviews +5. **Learn from Reviews**: Use reviews as learning opportunities +6. **Rotate Reviewers**: Spread knowledge and perspective across the team +7. **Review the Tests**: Tests are code too and deserve review + +## Related Rules + +- [development-workflow](./development-workflow.mdc): Development workflow guidelines +- [error-patterns](./error-patterns.mdc): Guidelines for identifying and resolving common error patterns +- [general-software](../general-software.mdc): General software development best practices + +## References + +- [Google's Engineering Practices Documentation](https://google.github.io/eng-practices/review/) +- [Thoughtbot's Code Review Guide](https://github.com/thoughtbot/guides/tree/main/code-review) +- [The Art of Readable Code](https://www.oreilly.com/library/view/the-art-of/9781449318482/) by Dustin Boswell and Trevor Foucher + +## Changelog + +- 1.0.0: Initial version +