# Feature Implementation Operation Complete full-stack feature implementation across database, backend, and frontend layers with production-ready code, tests, and documentation. ## Parameters **Received**: `$ARGUMENTS` (after removing 'implement' operation name) Expected format: `description:"feature details" [scope:"specific-area"] [priority:"high|medium|low"] [tests:"coverage-level"] [framework:"react|vue|angular"]` ## Workflow ### Phase 1: Requirements Understanding Parse the feature description and clarify: **Functional Requirements:** - What is the user-facing functionality? - What business problem does it solve? - What are the acceptance criteria? - What are the expected inputs and outputs? **Non-Functional Requirements:** - Performance expectations (response time, throughput) - Security considerations (authentication, authorization, data protection) - Scalability requirements (concurrent users, data volume) - UI/UX requirements (responsive, accessible, real-time updates) **Ask clarifying questions if:** - Requirements are ambiguous or incomplete - Multiple implementation approaches are possible - Technical constraints are unclear - Acceptance criteria are not well-defined ### Phase 2: Codebase Analysis Before implementation, examine the existing project: **Project Structure:** ```bash # Discover project layout ls -la find . -maxdepth 3 -type d | grep -E "(src|app|api|components|models)" # Identify tech stack find . -name "package.json" -o -name "requirements.txt" -o -name "go.mod" -o -name "pom.xml" cat package.json | grep -E "(react|vue|angular|express|fastify|prisma|typeorm)" ``` **Existing Patterns:** ```bash # Database patterns find . -path "*/migrations/*" -o -path "*/models/*" -o -path "*/schemas/*" # Backend patterns find . -path "*/services/*" -o -path "*/controllers/*" -o -path "*/routes/*" # Frontend patterns find . -path "*/components/*" -o -path "*/hooks/*" -o -path "*/store/*" # Testing patterns find . -path "*/__tests__/*" -o -path "*/test/*" -name "*.test.*" -o -name "*.spec.*" ``` **Conventions to Follow:** - Naming conventions (camelCase, PascalCase, snake_case) - File organization patterns - Import/export patterns - Error handling approaches - Testing frameworks and patterns - Documentation style ### Phase 3: Implementation Design Design the implementation across all layers: #### Database Design **Schema Design:** ```sql -- Example: User authentication feature CREATE TABLE users ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), email VARCHAR(255) UNIQUE NOT NULL, password_hash VARCHAR(255) NOT NULL, email_verified BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() ); CREATE TABLE user_sessions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, token_hash VARCHAR(255) NOT NULL, expires_at TIMESTAMP NOT NULL, created_at TIMESTAMP DEFAULT NOW(), INDEX idx_user_sessions_user_id (user_id), INDEX idx_user_sessions_token_hash (token_hash), INDEX idx_user_sessions_expires_at (expires_at) ); ``` **Index Strategy:** - Primary keys for unique identification - Foreign keys for relationships - Indexes on frequently queried columns - Composite indexes for multi-column queries - Consider query patterns and performance **Migration Planning:** - Forward migration (up) - Rollback migration (down) - Data seeding if needed - Migration testing strategy #### Backend Design **API Endpoint Design:** ```typescript // Example: Authentication endpoints POST /api/auth/register # Register new user POST /api/auth/login # Login with credentials POST /api/auth/logout # Logout current session POST /api/auth/refresh # Refresh access token GET /api/auth/me # Get current user profile POST /api/auth/verify-email # Verify email address POST /api/auth/forgot-password # Request password reset POST /api/auth/reset-password # Reset password with token ``` **Request/Response Models:** ```typescript // Register request interface RegisterRequest { email: string; password: string; name?: string; } // Register response interface RegisterResponse { user: { id: string; email: string; name: string | null; }; accessToken: string; refreshToken: string; } // Error response interface ErrorResponse { error: { code: string; message: string; details?: Record; }; } ``` **Service Layer Architecture:** - Business logic separated from controllers - Single responsibility per service - Dependency injection for testability - Error handling with custom exceptions - Validation at service boundaries **Data Access Layer:** - Repository pattern for data operations - Query builders or ORM usage - Transaction management - Connection pooling - Caching strategy #### Frontend Design **Component Structure:** ``` src/features/auth/ ├── components/ │ ├── LoginForm.tsx │ ├── RegisterForm.tsx │ ├── ForgotPasswordForm.tsx │ └── EmailVerification.tsx ├── hooks/ │ ├── useAuth.ts │ ├── useLogin.ts │ └── useRegister.ts ├── store/ │ ├── authSlice.ts # Redux/Zustand │ └── authSelectors.ts ├── api/ │ └── authApi.ts # API client ├── types/ │ └── auth.types.ts └── __tests__/ ├── LoginForm.test.tsx └── useAuth.test.ts ``` **State Management:** - Local state vs global state decisions - Server state management (React Query, SWR) - Form state (React Hook Form, Formik) - Authentication state persistence - Loading and error states **API Integration:** - HTTP client configuration (axios, fetch) - Request/response interceptors - Error handling and retry logic - Token management and refresh - API request cancellation ### Phase 4: Incremental Implementation Implement in phases for robustness and testability: #### Phase 4.1 - Data Layer Implementation **Step 1: Create Migration Script** ```sql -- migrations/20240101120000_add_user_authentication.up.sql BEGIN; CREATE TABLE users ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), email VARCHAR(255) UNIQUE NOT NULL, password_hash VARCHAR(255) NOT NULL, email_verified BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW(), CONSTRAINT email_format CHECK (email ~* '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}$') ); CREATE INDEX idx_users_email ON users(email); CREATE INDEX idx_users_created_at ON users(created_at); CREATE TABLE user_sessions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, token_hash VARCHAR(255) NOT NULL, expires_at TIMESTAMP NOT NULL, created_at TIMESTAMP DEFAULT NOW() ); CREATE INDEX idx_user_sessions_user_id ON user_sessions(user_id); CREATE INDEX idx_user_sessions_token_hash ON user_sessions(token_hash); CREATE INDEX idx_user_sessions_expires_at ON user_sessions(expires_at); COMMIT; ``` ```sql -- migrations/20240101120000_add_user_authentication.down.sql BEGIN; DROP TABLE IF EXISTS user_sessions; DROP TABLE IF EXISTS users; COMMIT; ``` **Step 2: Create/Update Models** ```typescript // models/User.ts import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, OneToMany } from 'typeorm'; import { UserSession } from './UserSession'; @Entity('users') export class User { @PrimaryGeneratedColumn('uuid') id: string; @Column({ type: 'varchar', length: 255, unique: true }) email: string; @Column({ type: 'varchar', length: 255, select: false }) passwordHash: string; @Column({ type: 'boolean', default: false }) emailVerified: boolean; @CreateDateColumn() createdAt: Date; @UpdateDateColumn() updatedAt: Date; @OneToMany(() => UserSession, session => session.user) sessions: UserSession[]; } ``` ```typescript // models/UserSession.ts import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, ManyToOne, JoinColumn, Index } from 'typeorm'; import { User } from './User'; @Entity('user_sessions') export class UserSession { @PrimaryGeneratedColumn('uuid') id: string; @Column({ type: 'uuid' }) @Index() userId: string; @Column({ type: 'varchar', length: 255 }) @Index() tokenHash: string; @Column({ type: 'timestamp' }) @Index() expiresAt: Date; @CreateDateColumn() createdAt: Date; @ManyToOne(() => User, user => user.sessions, { onDelete: 'CASCADE' }) @JoinColumn({ name: 'userId' }) user: User; } ``` **Step 3: Test Database Operations** ```typescript // models/__tests__/User.test.ts import { DataSource } from 'typeorm'; import { User } from '../User'; describe('User Model', () => { let dataSource: DataSource; beforeAll(async () => { dataSource = await createTestDataSource(); }); afterAll(async () => { await dataSource.destroy(); }); it('should create user with valid data', async () => { const userRepo = dataSource.getRepository(User); const user = userRepo.create({ email: 'test@example.com', passwordHash: 'hashed_password', }); await userRepo.save(user); expect(user.id).toBeDefined(); expect(user.email).toBe('test@example.com'); expect(user.emailVerified).toBe(false); }); it('should enforce unique email constraint', async () => { const userRepo = dataSource.getRepository(User); await userRepo.save({ email: 'duplicate@example.com', passwordHash: 'hash1', }); await expect( userRepo.save({ email: 'duplicate@example.com', passwordHash: 'hash2', }) ).rejects.toThrow(); }); }); ``` #### Phase 4.2 - Backend Layer Implementation **Step 1: Create Repository Layer** ```typescript // repositories/UserRepository.ts import { Repository } from 'typeorm'; import { User } from '../models/User'; import { AppDataSource } from '../config/database'; export class UserRepository { private repository: Repository; constructor() { this.repository = AppDataSource.getRepository(User); } async findByEmail(email: string): Promise { return this.repository.findOne({ where: { email: email.toLowerCase() }, select: ['id', 'email', 'passwordHash', 'emailVerified', 'createdAt', 'updatedAt'], }); } async findById(id: string): Promise { return this.repository.findOne({ where: { id }, }); } async create(data: { email: string; passwordHash: string }): Promise { const user = this.repository.create({ email: data.email.toLowerCase(), passwordHash: data.passwordHash, }); return this.repository.save(user); } async updateEmailVerified(userId: string, verified: boolean): Promise { await this.repository.update(userId, { emailVerified: verified }); } async updatePassword(userId: string, passwordHash: string): Promise { await this.repository.update(userId, { passwordHash }); } async delete(userId: string): Promise { await this.repository.delete(userId); } } ``` **Step 2: Create Service Layer** ```typescript // services/AuthService.ts import bcrypt from 'bcryptjs'; import jwt from 'jsonwebtoken'; import { UserRepository } from '../repositories/UserRepository'; import { SessionRepository } from '../repositories/SessionRepository'; import { UnauthorizedError, ConflictError, ValidationError } from '../errors'; export interface RegisterInput { email: string; password: string; name?: string; } export interface LoginInput { email: string; password: string; } export interface AuthTokens { accessToken: string; refreshToken: string; } export class AuthService { constructor( private userRepository: UserRepository, private sessionRepository: SessionRepository ) {} async register(input: RegisterInput): Promise<{ user: User; tokens: AuthTokens }> { // Validate input this.validateEmail(input.email); this.validatePassword(input.password); // Check if user exists const existingUser = await this.userRepository.findByEmail(input.email); if (existingUser) { throw new ConflictError('User with this email already exists'); } // Hash password const passwordHash = await bcrypt.hash(input.password, 12); // Create user const user = await this.userRepository.create({ email: input.email, passwordHash, }); // Generate tokens const tokens = await this.generateTokens(user.id); // Create session await this.sessionRepository.create({ userId: user.id, tokenHash: await this.hashToken(tokens.refreshToken), expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 days }); return { user, tokens }; } async login(input: LoginInput): Promise<{ user: User; tokens: AuthTokens }> { // Find user const user = await this.userRepository.findByEmail(input.email); if (!user) { throw new UnauthorizedError('Invalid credentials'); } // Verify password const isValid = await bcrypt.compare(input.password, user.passwordHash); if (!isValid) { throw new UnauthorizedError('Invalid credentials'); } // Generate tokens const tokens = await this.generateTokens(user.id); // Create session await this.sessionRepository.create({ userId: user.id, tokenHash: await this.hashToken(tokens.refreshToken), expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), }); return { user, tokens }; } async logout(refreshToken: string): Promise { const tokenHash = await this.hashToken(refreshToken); await this.sessionRepository.deleteByTokenHash(tokenHash); } async refreshTokens(refreshToken: string): Promise { // Verify refresh token const payload = jwt.verify(refreshToken, process.env.JWT_SECRET!) as { userId: string }; // Check if session exists const tokenHash = await this.hashToken(refreshToken); const session = await this.sessionRepository.findByTokenHash(tokenHash); if (!session || session.expiresAt < new Date()) { throw new UnauthorizedError('Invalid or expired refresh token'); } // Generate new tokens const tokens = await this.generateTokens(payload.userId); // Update session await this.sessionRepository.update(session.id, { tokenHash: await this.hashToken(tokens.refreshToken), expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), }); return tokens; } private async generateTokens(userId: string): Promise { const accessToken = jwt.sign( { userId, type: 'access' }, process.env.JWT_SECRET!, { expiresIn: '15m' } ); const refreshToken = jwt.sign( { userId, type: 'refresh' }, process.env.JWT_SECRET!, { expiresIn: '7d' } ); return { accessToken, refreshToken }; } private async hashToken(token: string): Promise { return bcrypt.hash(token, 10); } private validateEmail(email: string): void { const emailRegex = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}$/; if (!emailRegex.test(email)) { throw new ValidationError('Invalid email format'); } } private validatePassword(password: string): void { if (password.length < 8) { throw new ValidationError('Password must be at least 8 characters'); } if (!/[A-Z]/.test(password)) { throw new ValidationError('Password must contain at least one uppercase letter'); } if (!/[a-z]/.test(password)) { throw new ValidationError('Password must contain at least one lowercase letter'); } if (!/[0-9]/.test(password)) { throw new ValidationError('Password must contain at least one number'); } } } ``` **Step 3: Create API Controllers** ```typescript // controllers/AuthController.ts import { Request, Response, NextFunction } from 'express'; import { AuthService } from '../services/AuthService'; export class AuthController { constructor(private authService: AuthService) {} register = async (req: Request, res: Response, next: NextFunction) => { try { const { email, password, name } = req.body; const result = await this.authService.register({ email, password, name, }); res.status(201).json({ user: { id: result.user.id, email: result.user.email, emailVerified: result.user.emailVerified, }, accessToken: result.tokens.accessToken, refreshToken: result.tokens.refreshToken, }); } catch (error) { next(error); } }; login = async (req: Request, res: Response, next: NextFunction) => { try { const { email, password } = req.body; const result = await this.authService.login({ email, password }); res.json({ user: { id: result.user.id, email: result.user.email, emailVerified: result.user.emailVerified, }, accessToken: result.tokens.accessToken, refreshToken: result.tokens.refreshToken, }); } catch (error) { next(error); } }; logout = async (req: Request, res: Response, next: NextFunction) => { try { const { refreshToken } = req.body; await this.authService.logout(refreshToken); res.status(204).send(); } catch (error) { next(error); } }; refresh = async (req: Request, res: Response, next: NextFunction) => { try { const { refreshToken } = req.body; const tokens = await this.authService.refreshTokens(refreshToken); res.json(tokens); } catch (error) { next(error); } }; me = async (req: Request, res: Response, next: NextFunction) => { try { // User is attached by auth middleware const user = req.user!; res.json({ id: user.id, email: user.email, emailVerified: user.emailVerified, createdAt: user.createdAt, }); } catch (error) { next(error); } }; } ``` **Step 4: Create Routes** ```typescript // routes/auth.routes.ts import { Router } from 'express'; import { AuthController } from '../controllers/AuthController'; import { AuthService } from '../services/AuthService'; import { UserRepository } from '../repositories/UserRepository'; import { SessionRepository } from '../repositories/SessionRepository'; import { authenticate } from '../middlewares/auth.middleware'; import { validateRequest } from '../middlewares/validation.middleware'; import { registerSchema, loginSchema, refreshSchema } from '../schemas/auth.schemas'; const router = Router(); // Initialize dependencies const userRepository = new UserRepository(); const sessionRepository = new SessionRepository(); const authService = new AuthService(userRepository, sessionRepository); const authController = new AuthController(authService); // Public routes router.post('/register', validateRequest(registerSchema), authController.register); router.post('/login', validateRequest(loginSchema), authController.login); router.post('/refresh', validateRequest(refreshSchema), authController.refresh); // Protected routes router.post('/logout', authenticate, authController.logout); router.get('/me', authenticate, authController.me); export default router; ``` **Step 5: Write Tests** ```typescript // services/__tests__/AuthService.test.ts import { AuthService } from '../AuthService'; import { UserRepository } from '../../repositories/UserRepository'; import { SessionRepository } from '../../repositories/SessionRepository'; import { ConflictError, UnauthorizedError, ValidationError } from '../../errors'; describe('AuthService', () => { let authService: AuthService; let userRepository: jest.Mocked; let sessionRepository: jest.Mocked; beforeEach(() => { userRepository = { findByEmail: jest.fn(), create: jest.fn(), findById: jest.fn(), } as any; sessionRepository = { create: jest.fn(), findByTokenHash: jest.fn(), update: jest.fn(), deleteByTokenHash: jest.fn(), } as any; authService = new AuthService(userRepository, sessionRepository); }); describe('register', () => { it('should register new user successfully', async () => { const input = { email: 'test@example.com', password: 'Password123', }; userRepository.findByEmail.mockResolvedValue(null); userRepository.create.mockResolvedValue({ id: 'user-id', email: input.email, emailVerified: false, createdAt: new Date(), updatedAt: new Date(), } as any); const result = await authService.register(input); expect(result.user).toBeDefined(); expect(result.tokens.accessToken).toBeDefined(); expect(result.tokens.refreshToken).toBeDefined(); expect(userRepository.create).toHaveBeenCalled(); expect(sessionRepository.create).toHaveBeenCalled(); }); it('should throw ConflictError if user exists', async () => { userRepository.findByEmail.mockResolvedValue({ id: 'existing-id' } as any); await expect( authService.register({ email: 'existing@example.com', password: 'Password123', }) ).rejects.toThrow(ConflictError); }); it('should throw ValidationError for invalid email', async () => { await expect( authService.register({ email: 'invalid-email', password: 'Password123', }) ).rejects.toThrow(ValidationError); }); it('should throw ValidationError for weak password', async () => { await expect( authService.register({ email: 'test@example.com', password: 'weak', }) ).rejects.toThrow(ValidationError); }); }); describe('login', () => { it('should login user successfully', async () => { const user = { id: 'user-id', email: 'test@example.com', passwordHash: await bcrypt.hash('Password123', 12), }; userRepository.findByEmail.mockResolvedValue(user as any); const result = await authService.login({ email: 'test@example.com', password: 'Password123', }); expect(result.user).toBeDefined(); expect(result.tokens).toBeDefined(); expect(sessionRepository.create).toHaveBeenCalled(); }); it('should throw UnauthorizedError for invalid credentials', async () => { userRepository.findByEmail.mockResolvedValue(null); await expect( authService.login({ email: 'test@example.com', password: 'Password123', }) ).rejects.toThrow(UnauthorizedError); }); }); }); ``` ```typescript // controllers/__tests__/AuthController.test.ts import request from 'supertest'; import { app } from '../../app'; import { UserRepository } from '../../repositories/UserRepository'; describe('AuthController', () => { let userRepository: UserRepository; beforeEach(async () => { await clearDatabase(); userRepository = new UserRepository(); }); describe('POST /api/auth/register', () => { it('should register new user', async () => { const response = await request(app) .post('/api/auth/register') .send({ email: 'newuser@example.com', password: 'Password123', }) .expect(201); expect(response.body).toHaveProperty('user'); expect(response.body).toHaveProperty('accessToken'); expect(response.body).toHaveProperty('refreshToken'); expect(response.body.user.email).toBe('newuser@example.com'); }); it('should return 409 for duplicate email', async () => { await userRepository.create({ email: 'existing@example.com', passwordHash: 'hash', }); await request(app) .post('/api/auth/register') .send({ email: 'existing@example.com', password: 'Password123', }) .expect(409); }); it('should return 400 for invalid input', async () => { await request(app) .post('/api/auth/register') .send({ email: 'invalid-email', password: 'weak', }) .expect(400); }); }); describe('POST /api/auth/login', () => { it('should login existing user', async () => { // Create user first await request(app) .post('/api/auth/register') .send({ email: 'test@example.com', password: 'Password123', }); const response = await request(app) .post('/api/auth/login') .send({ email: 'test@example.com', password: 'Password123', }) .expect(200); expect(response.body).toHaveProperty('accessToken'); expect(response.body).toHaveProperty('refreshToken'); }); it('should return 401 for invalid credentials', async () => { await request(app) .post('/api/auth/login') .send({ email: 'nonexistent@example.com', password: 'Password123', }) .expect(401); }); }); describe('GET /api/auth/me', () => { it('should return current user', async () => { // Register and login const registerResponse = await request(app) .post('/api/auth/register') .send({ email: 'test@example.com', password: 'Password123', }); const { accessToken } = registerResponse.body; const response = await request(app) .get('/api/auth/me') .set('Authorization', `Bearer ${accessToken}`) .expect(200); expect(response.body.email).toBe('test@example.com'); }); it('should return 401 without token', async () => { await request(app) .get('/api/auth/me') .expect(401); }); }); }); ``` #### Phase 4.3 - Frontend Layer Implementation **Step 1: Create API Client** ```typescript // src/features/auth/api/authApi.ts import axios, { AxiosInstance } from 'axios'; export interface RegisterRequest { email: string; password: string; name?: string; } export interface LoginRequest { email: string; password: string; } export interface AuthResponse { user: { id: string; email: string; emailVerified: boolean; }; accessToken: string; refreshToken: string; } export interface RefreshTokenResponse { accessToken: string; refreshToken: string; } export interface UserProfile { id: string; email: string; emailVerified: boolean; createdAt: string; } export class AuthApi { private client: AxiosInstance; constructor(baseURL: string = '/api') { this.client = axios.create({ baseURL, timeout: 10000, headers: { 'Content-Type': 'application/json', }, }); // Request interceptor to add auth token this.client.interceptors.request.use((config) => { const token = localStorage.getItem('accessToken'); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }); // Response interceptor to handle token refresh this.client.interceptors.response.use( (response) => response, async (error) => { const originalRequest = error.config; // If 401 and not already retried, try to refresh token if (error.response?.status === 401 && !originalRequest._retry) { originalRequest._retry = true; try { const refreshToken = localStorage.getItem('refreshToken'); if (refreshToken) { const response = await this.refreshTokens({ refreshToken }); localStorage.setItem('accessToken', response.accessToken); localStorage.setItem('refreshToken', response.refreshToken); // Retry original request with new token originalRequest.headers.Authorization = `Bearer ${response.accessToken}`; return this.client(originalRequest); } } catch (refreshError) { // Refresh failed, clear tokens and redirect to login localStorage.removeItem('accessToken'); localStorage.removeItem('refreshToken'); window.location.href = '/login'; throw refreshError; } } throw error; } ); } async register(data: RegisterRequest): Promise { const response = await this.client.post('/auth/register', data); return response.data; } async login(data: LoginRequest): Promise { const response = await this.client.post('/auth/login', data); return response.data; } async logout(): Promise { const refreshToken = localStorage.getItem('refreshToken'); await this.client.post('/auth/logout', { refreshToken }); } async refreshTokens(data: { refreshToken: string }): Promise { const response = await this.client.post('/auth/refresh', data); return response.data; } async getCurrentUser(): Promise { const response = await this.client.get('/auth/me'); return response.data; } } export const authApi = new AuthApi(); ``` **Step 2: Create React Hooks** ```typescript // src/features/auth/hooks/useAuth.ts import { create } from 'zustand'; import { persist } from 'zustand/middleware'; import { authApi } from '../api/authApi'; interface User { id: string; email: string; emailVerified: boolean; } interface AuthState { user: User | null; isAuthenticated: boolean; isLoading: boolean; error: string | null; login: (email: string, password: string) => Promise; register: (email: string, password: string, name?: string) => Promise; logout: () => Promise; refreshUser: () => Promise; clearError: () => void; } export const useAuth = create()( persist( (set, get) => ({ user: null, isAuthenticated: false, isLoading: false, error: null, login: async (email: string, password: string) => { set({ isLoading: true, error: null }); try { const response = await authApi.login({ email, password }); localStorage.setItem('accessToken', response.accessToken); localStorage.setItem('refreshToken', response.refreshToken); set({ user: response.user, isAuthenticated: true, isLoading: false, }); } catch (error: any) { const errorMessage = error.response?.data?.error?.message || 'Login failed'; set({ error: errorMessage, isLoading: false }); throw error; } }, register: async (email: string, password: string, name?: string) => { set({ isLoading: true, error: null }); try { const response = await authApi.register({ email, password, name }); localStorage.setItem('accessToken', response.accessToken); localStorage.setItem('refreshToken', response.refreshToken); set({ user: response.user, isAuthenticated: true, isLoading: false, }); } catch (error: any) { const errorMessage = error.response?.data?.error?.message || 'Registration failed'; set({ error: errorMessage, isLoading: false }); throw error; } }, logout: async () => { set({ isLoading: true }); try { await authApi.logout(); } catch (error) { console.error('Logout error:', error); } finally { localStorage.removeItem('accessToken'); localStorage.removeItem('refreshToken'); set({ user: null, isAuthenticated: false, isLoading: false, error: null, }); } }, refreshUser: async () => { if (!get().isAuthenticated) return; try { const user = await authApi.getCurrentUser(); set({ user }); } catch (error) { console.error('Failed to refresh user:', error); } }, clearError: () => set({ error: null }), }), { name: 'auth-storage', partialize: (state) => ({ user: state.user, isAuthenticated: state.isAuthenticated, }), } ) ); ``` **Step 3: Create Components** ```typescript // src/features/auth/components/LoginForm.tsx import React from 'react'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; import { useAuth } from '../hooks/useAuth'; import { useNavigate } from 'react-router-dom'; const loginSchema = z.object({ email: z.string().email('Invalid email address'), password: z.string().min(8, 'Password must be at least 8 characters'), }); type LoginFormData = z.infer; export const LoginForm: React.FC = () => { const { login, isLoading, error, clearError } = useAuth(); const navigate = useNavigate(); const { register, handleSubmit, formState: { errors }, } = useForm({ resolver: zodResolver(loginSchema), }); const onSubmit = async (data: LoginFormData) => { try { clearError(); await login(data.email, data.password); navigate('/dashboard'); } catch (error) { // Error is handled by the store } }; return (

Login

{error && (
{error}
)}
{errors.email && (

{errors.email.message}

)}
{errors.password && (

{errors.password.message}

)}
); }; ``` ```typescript // src/features/auth/components/RegisterForm.tsx import React from 'react'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; import { useAuth } from '../hooks/useAuth'; import { useNavigate } from 'react-router-dom'; const registerSchema = z.object({ email: z.string().email('Invalid email address'), password: z .string() .min(8, 'Password must be at least 8 characters') .regex(/[A-Z]/, 'Password must contain at least one uppercase letter') .regex(/[a-z]/, 'Password must contain at least one lowercase letter') .regex(/[0-9]/, 'Password must contain at least one number'), confirmPassword: z.string(), name: z.string().optional(), }).refine((data) => data.password === data.confirmPassword, { message: "Passwords don't match", path: ['confirmPassword'], }); type RegisterFormData = z.infer; export const RegisterForm: React.FC = () => { const { register: registerUser, isLoading, error, clearError } = useAuth(); const navigate = useNavigate(); const { register, handleSubmit, formState: { errors }, } = useForm({ resolver: zodResolver(registerSchema), }); const onSubmit = async (data: RegisterFormData) => { try { clearError(); await registerUser(data.email, data.password, data.name); navigate('/dashboard'); } catch (error) { // Error is handled by the store } }; return (

Create Account

{error && (
{error}
)}
{errors.email && (

{errors.email.message}

)}
{errors.password && (

{errors.password.message}

)}
{errors.confirmPassword && (

{errors.confirmPassword.message}

)}
); }; ``` ```typescript // src/features/auth/components/ProtectedRoute.tsx import React from 'react'; import { Navigate, Outlet } from 'react-router-dom'; import { useAuth } from '../hooks/useAuth'; export const ProtectedRoute: React.FC = () => { const { isAuthenticated, isLoading } = useAuth(); if (isLoading) { return (
Loading...
); } return isAuthenticated ? : ; }; ``` **Step 4: Write Component Tests** ```typescript // src/features/auth/components/__tests__/LoginForm.test.tsx import React from 'react'; import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import { LoginForm } from '../LoginForm'; import { useAuth } from '../../hooks/useAuth'; import { BrowserRouter } from 'react-router-dom'; jest.mock('../../hooks/useAuth'); const mockUseAuth = useAuth as jest.MockedFunction; describe('LoginForm', () => { const mockLogin = jest.fn(); const mockClearError = jest.fn(); beforeEach(() => { mockUseAuth.mockReturnValue({ login: mockLogin, isLoading: false, error: null, clearError: mockClearError, } as any); }); afterEach(() => { jest.clearAllMocks(); }); const renderLoginForm = () => { return render( ); }; it('should render login form', () => { renderLoginForm(); expect(screen.getByLabelText(/email/i)).toBeInTheDocument(); expect(screen.getByLabelText(/password/i)).toBeInTheDocument(); expect(screen.getByRole('button', { name: /login/i })).toBeInTheDocument(); }); it('should show validation errors for invalid input', async () => { renderLoginForm(); const submitButton = screen.getByRole('button', { name: /login/i }); fireEvent.click(submitButton); await waitFor(() => { expect(screen.getByText(/invalid email address/i)).toBeInTheDocument(); }); }); it('should call login with valid credentials', async () => { mockLogin.mockResolvedValue(undefined); renderLoginForm(); fireEvent.change(screen.getByLabelText(/email/i), { target: { value: 'test@example.com' }, }); fireEvent.change(screen.getByLabelText(/password/i), { target: { value: 'Password123' }, }); const submitButton = screen.getByRole('button', { name: /login/i }); fireEvent.click(submitButton); await waitFor(() => { expect(mockLogin).toHaveBeenCalledWith('test@example.com', 'Password123'); }); }); it('should display error message on login failure', () => { mockUseAuth.mockReturnValue({ login: mockLogin, isLoading: false, error: 'Invalid credentials', clearError: mockClearError, } as any); renderLoginForm(); expect(screen.getByText(/invalid credentials/i)).toBeInTheDocument(); }); it('should disable form during loading', () => { mockUseAuth.mockReturnValue({ login: mockLogin, isLoading: true, error: null, clearError: mockClearError, } as any); renderLoginForm(); expect(screen.getByLabelText(/email/i)).toBeDisabled(); expect(screen.getByLabelText(/password/i)).toBeDisabled(); expect(screen.getByRole('button', { name: /logging in/i })).toBeDisabled(); }); }); ``` #### Phase 4.4 - Integration & Polish **Step 1: End-to-End Tests** ```typescript // e2e/auth.spec.ts import { test, expect } from '@playwright/test'; test.describe('Authentication Flow', () => { test.beforeEach(async ({ page }) => { await page.goto('http://localhost:3000'); }); test('should complete full registration flow', async ({ page }) => { // Navigate to register page await page.click('text=Register'); // Fill registration form await page.fill('input[name="name"]', 'Test User'); await page.fill('input[name="email"]', `test-${Date.now()}@example.com`); await page.fill('input[name="password"]', 'Password123'); await page.fill('input[name="confirmPassword"]', 'Password123'); // Submit form await page.click('button:has-text("Register")'); // Should redirect to dashboard await expect(page).toHaveURL(/\/dashboard/); await expect(page.locator('text=Welcome')).toBeVisible(); }); test('should complete full login flow', async ({ page }) => { // Assume user already registered await page.click('text=Login'); // Fill login form await page.fill('input[name="email"]', 'existing@example.com'); await page.fill('input[name="password"]', 'Password123'); // Submit form await page.click('button:has-text("Login")'); // Should redirect to dashboard await expect(page).toHaveURL(/\/dashboard/); }); test('should handle logout correctly', async ({ page }) => { // Login first await page.click('text=Login'); await page.fill('input[name="email"]', 'existing@example.com'); await page.fill('input[name="password"]', 'Password123'); await page.click('button:has-text("Login")'); await expect(page).toHaveURL(/\/dashboard/); // Logout await page.click('button:has-text("Logout")'); // Should redirect to home/login await expect(page).toHaveURL(/\/(login)?$/); }); test('should protect routes when not authenticated', async ({ page }) => { // Try to access protected route await page.goto('http://localhost:3000/dashboard'); // Should redirect to login await expect(page).toHaveURL(/\/login/); }); test('should refresh token automatically', async ({ page, context }) => { // Login await page.click('text=Login'); await page.fill('input[name="email"]', 'existing@example.com'); await page.fill('input[name="password"]', 'Password123'); await page.click('button:has-text("Login")'); // Get initial access token const initialStorage = await context.storageState(); const initialToken = initialStorage.origins[0]?.localStorage.find( (item) => item.name === 'accessToken' )?.value; // Wait for token to expire (in real scenario, this would be 15 minutes) // For testing, you might mock the token expiration await page.waitForTimeout(16 * 60 * 1000); // 16 minutes // Make an API request that should trigger token refresh await page.reload(); // Get new access token const newStorage = await context.storageState(); const newToken = newStorage.origins[0]?.localStorage.find( (item) => item.name === 'accessToken' )?.value; // Tokens should be different expect(newToken).not.toBe(initialToken); }); }); ``` **Step 2: Performance Optimization** ```typescript // Performance considerations for authentication feature // 1. Database Indexes (already included in migration) // - idx_users_email for login lookups // - idx_user_sessions_user_id for session queries // - idx_user_sessions_token_hash for token validation // - idx_user_sessions_expires_at for cleanup queries // 2. Caching Strategy // Example: Redis caching for user sessions import { Redis } from 'ioredis'; const redis = new Redis(process.env.REDIS_URL); // Cache user data after login async function cacheUserSession(userId: string, userData: any) { await redis.setex( `user:${userId}`, 15 * 60, // 15 minutes (access token lifetime) JSON.stringify(userData) ); } // Get cached user data async function getCachedUser(userId: string) { const cached = await redis.get(`user:${userId}`); return cached ? JSON.parse(cached) : null; } // 3. Password Hashing Optimization // Use bcrypt with work factor 12 (balance security and performance) const BCRYPT_ROUNDS = 12; // 4. Token Generation Optimization // Use JWT with short expiration for access tokens (15 minutes) // Use longer expiration for refresh tokens (7 days) // 5. Connection Pooling // Configure database connection pool { type: 'postgres', host: process.env.DB_HOST, port: Number(process.env.DB_PORT), username: process.env.DB_USER, password: process.env.DB_PASSWORD, database: process.env.DB_NAME, extra: { max: 20, // Maximum pool size min: 5, // Minimum pool size idle: 10000, // Idle timeout acquire: 30000, // Acquire timeout } } // 6. Frontend Optimization // - Use React.memo for authentication components // - Lazy load protected routes // - Implement request deduplication // Example: Lazy loading const Dashboard = React.lazy(() => import('./pages/Dashboard')); const Profile = React.lazy(() => import('./pages/Profile')); // Example: Request deduplication import { useQuery } from '@tanstack/react-query'; function useCurrentUser() { return useQuery({ queryKey: ['currentUser'], queryFn: () => authApi.getCurrentUser(), staleTime: 5 * 60 * 1000, // 5 minutes cacheTime: 10 * 60 * 1000, // 10 minutes }); } ``` **Step 3: Security Hardening** ```typescript // Security measures for authentication feature // 1. Rate Limiting import rateLimit from 'express-rate-limit'; const loginLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 5, // 5 requests per window message: 'Too many login attempts, please try again later', standardHeaders: true, legacyHeaders: false, }); app.use('/api/auth/login', loginLimiter); // 2. Input Sanitization import validator from 'validator'; import xss from 'xss'; function sanitizeInput(input: string): string { return xss(validator.trim(input)); } // 3. SQL Injection Prevention (using parameterized queries with TypeORM) // TypeORM automatically uses parameterized queries // 4. XSS Prevention // - Set security headers import helmet from 'helmet'; app.use(helmet()); // - Content Security Policy app.use(helmet.contentSecurityPolicy({ directives: { defaultSrc: ["'self'"], scriptSrc: ["'self'", "'unsafe-inline'"], styleSrc: ["'self'", "'unsafe-inline'"], imgSrc: ["'self'", 'data:', 'https:'], }, })); // 5. CSRF Protection import csrf from 'csurf'; const csrfProtection = csrf({ cookie: true }); app.use(csrfProtection); // 6. Secure Cookie Configuration app.use(session({ secret: process.env.SESSION_SECRET!, resave: false, saveUninitialized: false, cookie: { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'strict', maxAge: 24 * 60 * 60 * 1000, // 24 hours }, })); // 7. Password Policy Enforcement const PASSWORD_REQUIREMENTS = { minLength: 8, requireUppercase: true, requireLowercase: true, requireNumber: true, requireSpecialChar: false, // Optional }; // 8. Account Lockout (after failed attempts) async function checkAccountLockout(email: string): Promise { const attempts = await redis.get(`login_attempts:${email}`); if (attempts && parseInt(attempts) >= 5) { const ttl = await redis.ttl(`login_attempts:${email}`); if (ttl > 0) { throw new Error(`Account locked. Try again in ${Math.ceil(ttl / 60)} minutes`); } } return false; } async function recordFailedLogin(email: string): Promise { const key = `login_attempts:${email}`; const attempts = await redis.incr(key); if (attempts === 1) { await redis.expire(key, 15 * 60); // 15 minutes } } // 9. Session Management // - Implement session rotation on privilege escalation // - Invalidate sessions on password change // - Implement "logout all devices" functionality async function invalidateAllUserSessions(userId: string): Promise { await sessionRepository.deleteAllByUserId(userId); await redis.del(`user:${userId}`); } ``` **Step 4: Documentation** ```markdown # Authentication Feature Documentation ## Overview Complete user authentication system with email/password login, JWT tokens, session management, and secure password handling. ## Features - User registration with email verification - Login with email and password - JWT-based authentication (access + refresh tokens) - Token refresh mechanism - Secure session management - Password hashing with bcrypt - Rate limiting on authentication endpoints - CSRF protection - Account lockout after failed attempts ## API Endpoints ### POST /api/auth/register Register a new user account. **Request Body:** ```json { "email": "user@example.com", "password": "SecurePassword123", "name": "John Doe" } ``` **Response (201 Created):** ```json { "user": { "id": "uuid", "email": "user@example.com", "emailVerified": false }, "accessToken": "jwt-access-token", "refreshToken": "jwt-refresh-token" } ``` **Error Responses:** - 400 Bad Request - Invalid input - 409 Conflict - Email already exists ### POST /api/auth/login Login with existing credentials. **Request Body:** ```json { "email": "user@example.com", "password": "SecurePassword123" } ``` **Response (200 OK):** ```json { "user": { "id": "uuid", "email": "user@example.com", "emailVerified": true }, "accessToken": "jwt-access-token", "refreshToken": "jwt-refresh-token" } ``` **Error Responses:** - 401 Unauthorized - Invalid credentials - 429 Too Many Requests - Rate limit exceeded ### POST /api/auth/refresh Refresh access token using refresh token. **Request Body:** ```json { "refreshToken": "jwt-refresh-token" } ``` **Response (200 OK):** ```json { "accessToken": "new-jwt-access-token", "refreshToken": "new-jwt-refresh-token" } ``` ### POST /api/auth/logout Logout and invalidate current session. **Headers:** ``` Authorization: Bearer {accessToken} ``` **Request Body:** ```json { "refreshToken": "jwt-refresh-token" } ``` **Response (204 No Content)** ### GET /api/auth/me Get current user profile. **Headers:** ``` Authorization: Bearer {accessToken} ``` **Response (200 OK):** ```json { "id": "uuid", "email": "user@example.com", "emailVerified": true, "createdAt": "2024-01-01T00:00:00Z" } ``` ## Frontend Usage ### Using the Auth Hook ```typescript import { useAuth } from '@/features/auth/hooks/useAuth'; function MyComponent() { const { user, isAuthenticated, login, logout } = useAuth(); const handleLogin = async () => { try { await login('user@example.com', 'password'); // User is now logged in } catch (error) { // Handle error } }; return (
{isAuthenticated ? ( <>

Welcome, {user?.email}

) : ( )}
); } ``` ### Protecting Routes ```typescript import { ProtectedRoute } from '@/features/auth/components/ProtectedRoute'; } /> } /> }> } /> } /> ``` ## Security Considerations 1. **Password Requirements:** - Minimum 8 characters - At least one uppercase letter - At least one lowercase letter - At least one number 2. **Token Lifetimes:** - Access token: 15 minutes - Refresh token: 7 days 3. **Rate Limiting:** - Login: 5 attempts per 15 minutes - Register: 3 attempts per hour 4. **Session Management:** - Sessions are invalidated on logout - Sessions are automatically cleaned up after expiration - Multiple sessions per user are allowed ## Testing Run tests with: ```bash # Unit tests npm run test # Integration tests npm run test:integration # E2E tests npm run test:e2e ``` ## Environment Variables ```env # Database DB_HOST=localhost DB_PORT=5432 DB_USER=postgres DB_PASSWORD=password DB_NAME=myapp # JWT JWT_SECRET=your-secret-key-change-in-production # Session SESSION_SECRET=your-session-secret # Redis (for rate limiting and caching) REDIS_URL=redis://localhost:6379 # SMTP (for email verification) SMTP_HOST=smtp.example.com SMTP_PORT=587 SMTP_USER=your-email@example.com SMTP_PASSWORD=your-smtp-password ``` ## Deployment 1. Run migrations: ```bash npm run migrate ``` 2. Build the application: ```bash npm run build ``` 3. Start the server: ```bash npm start ``` ## Troubleshooting ### Token expired error If you receive "Token expired" errors, ensure: - The JWT_SECRET is correctly set - System clocks are synchronized - Token refresh is working correctly ### Cannot login If login fails: - Verify database connection - Check password hashing (bcrypt rounds) - Verify rate limiting isn't blocking requests - Check error logs for details ### CORS issues If frontend cannot connect to backend: - Verify CORS configuration in backend - Ensure credentials are included in requests - Check allowed origins match frontend URL ``` ## Output Format Provide complete implementation organized by layer: ```markdown # Feature Implementation: {Feature Name} ## Overview {Brief description and purpose} ## Implementation Summary {High-level approach and key decisions} ## Phase 1: Database Layer ### Migration Script {SQL migration with up/down} ### Models/Schemas {Data models with relationships} ### Testing {Database operation tests} ## Phase 2: Backend Layer ### Data Access Layer {Repositories and queries} ### Business Logic Layer {Services with business rules} ### API Layer {Controllers and routes} ### API Documentation {Endpoint specs with examples} ### Testing {Unit and integration tests} ## Phase 3: Frontend Layer ### API Client {HTTP client with interceptors} ### State Management {Hooks and stores} ### Components {React/Vue/Angular components} ### Testing {Component tests} ## Phase 4: Integration & Polish ### E2E Tests {End-to-end test scenarios} ### Performance Optimizations {Caching, indexing, lazy loading} ### Security Hardening {Rate limiting, validation, CSRF} ### Documentation {API docs, usage guide, troubleshooting} ## Configuration Changes ### Environment Variables {New env vars needed} ### Dependencies {New packages to install} ## Deployment Considerations {Migration steps, monitoring, rollback} ## Follow-up Tasks {Future improvements} ``` ## Quality Checklist Before considering the feature complete: - [ ] All layers implemented (database, backend, frontend) - [ ] Database migrations tested (up and down) - [ ] Unit tests written and passing (>80% coverage) - [ ] Integration tests written and passing - [ ] E2E tests for critical flows - [ ] Error handling comprehensive - [ ] Validation on both frontend and backend - [ ] Security measures implemented (auth, rate limiting, CSRF) - [ ] Performance optimized (indexes, caching, lazy loading) - [ ] Accessibility considered (WCAG 2.1 AA) - [ ] Documentation complete (API docs, usage guide) - [ ] No hardcoded secrets or sensitive data - [ ] Code follows project conventions - [ ] Ready for code review ## Error Handling **Unclear Requirements:** - Ask specific questions about acceptance criteria - Request clarification on edge cases - Provide examples to confirm understanding - Suggest sensible defaults if context missing **Missing Context:** - List needed information (tech stack, patterns) - Attempt to discover from codebase - Document assumptions made - Provide alternatives if context unclear **Implementation Blockers:** - Clearly identify the blocker - Suggest alternative approaches - Provide workarounds if available - Document issue for resolution - Continue with unblocked portions ## Examples by Feature Type ### Real-time Features (WebSocket/SSE) - WebSocket connection management - Event broadcasting and subscriptions - Presence tracking - Conflict resolution - Reconnection handling ### Payment Features (Stripe/PayPal) - Checkout flow integration - Webhook handling - Payment method management - Subscription management - Invoice generation - Refund processing ### File Upload Features - Multipart form handling - File validation (type, size) - Storage integration (S3, GCS) - Progress tracking - Image optimization - Virus scanning ### Search Features - Full-text search implementation - Filter and sorting - Pagination - Search suggestions - Relevance scoring - Query optimization