Files

14 KiB

Let's Event - Technical Documentation

Table of Contents

  1. System Architecture
  2. Tech Stack
  3. Database Schema
  4. Authentication
  5. API Routes
  6. Development Setup
  7. Testing Strategy
  8. CI/CD Pipeline
  9. Deployment

System Architecture

Overview

Let's Event is a meeting scheduling application built with Next.js 16, using server-side rendering and server actions for optimal performance and user experience.

Architecture Layers

┌─────────────────────────────────────────┐
│         Client (Browser)                 │
│  - React Components (Server/Client)      │
│  - Form Actions                          │
└───────────────┬─────────────────────────┘
                │
┌───────────────▼─────────────────────────┐
│         Next.js App Router               │
│  - Server Components                     │
│  - Server Actions                        │
│  - API Routes (NextAuth)                 │
└───────────────┬─────────────────────────┘
                │
┌───────────────▼─────────────────────────┐
│         Business Logic Layer             │
│  - Auth (NextAuth v5)                    │
│  - Prisma ORM                            │
└───────────────┬─────────────────────────┘
                │
┌───────────────▼─────────────────────────┐
│         Data Layer                       │
│  - PostgreSQL 16                         │
│  - Prisma Adapter (pg)                   │
└─────────────────────────────────────────┘

Key Design Decisions

  1. Server-First Architecture: Leverages Next.js server components and actions to minimize client-side JavaScript
  2. Type Safety: Full TypeScript coverage with Prisma generating types from database schema
  3. Security: Credentials-based authentication with bcrypt password hashing
  4. Performance: PostgreSQL connection pooling via Prisma adapter

Tech Stack

Core Framework

  • Next.js 16.1.6 (App Router with Turbopack)
  • React 19.2.3 (Server Components)
  • TypeScript 5.x

Database & ORM

  • PostgreSQL 16 (via Docker)
  • Prisma 7.4.1 (ORM with pg adapter)
  • @prisma/adapter-pg (Connection pooling)

Authentication

  • NextAuth v5.0.0-beta.30 (Credentials Provider)
  • bcryptjs 3.0.3 (Password hashing)

UI Components

  • shadcn/ui (Radix UI primitives)
  • Tailwind CSS 4 (Styling)
  • Lucide React (Icons)

Forms & Validation

  • React Hook Form 7.71.2
  • Zod 4.3.6 (Schema validation)

Testing

  • Playwright 1.58.2 (E2E tests)
  • Vitest 4.0.18 (Unit tests)

Code Quality

  • Biome 2.4.4 (Linting & Formatting)
  • Husky 9.1.7 (Git hooks)

Database Schema

Overview

Prisma schema with 5 main models following NextAuth adapter conventions.

Entity Relationship Diagram

User ──┬── Account (OAuth accounts)
       ├── Session (Active sessions)
       ├── Availability (Time slots)
       ├── EventType (Meeting types)
       └── Booking (as attendee or organizer)

Models

User

model User {
  id            String    @id @default(cuid())
  name          String?
  email         String    @unique
  emailVerified DateTime?
  image         String?
  username      String?   @unique
  timezone      String    @default("America/Sao_Paulo")
  createdAt     DateTime  @default(now())
  updatedAt     DateTime  @updatedAt
  
  // Relations
  accounts      Account[]
  sessions      Session[]
  availability  Availability[]
  eventTypes    EventType[]
  bookings      Booking[]
}

Purpose: Central user entity with profile and authentication data.

Key Fields:

  • timezone: User's preferred timezone (default: São Paulo)
  • username: Unique slug for booking URLs (e.g., /book/johndoe)
  • emailVerified: Tracks email verification status

Availability

model Availability {
  id        String   @id @default(cuid())
  userId    String
  dayOfWeek Int      // 0 = Sunday, 6 = Saturday
  startTime String   // HH:MM format
  endTime   String   // HH:MM format
  createdAt DateTime @default(now())
  
  user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  
  @@index([userId])
}

Purpose: Defines when users are available for meetings.

Business Rules:

  • dayOfWeek: 0-6 (Sunday to Saturday)
  • startTime/endTime: 24-hour format (e.g., "09:00", "17:30")
  • Cascades on user deletion

EventType

model EventType {
  id          String   @id @default(cuid())
  userId      String
  title       String
  slug        String
  description String?
  duration    Int      // Minutes
  active      Boolean  @default(true)
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt
  
  user     User      @relation(fields: [userId], references: [id], onDelete: Cascade)
  bookings Booking[]
  
  @@unique([userId, slug])
  @@index([userId])
}

Purpose: Different types of meetings users can offer.

Examples:

  • 30-minute consultation
  • 1-hour project planning
  • 15-minute quick chat

Booking

model Booking {
  id          String   @id @default(cuid())
  userId      String
  eventTypeId String
  startTime   DateTime
  endTime     DateTime
  attendeeName  String
  attendeeEmail String
  status      String   @default("pending") // pending, confirmed, cancelled
  createdAt   DateTime @default(now())
  
  user      User      @relation(fields: [userId], references: [id], onDelete: Cascade)
  eventType EventType @relation(fields: [eventTypeId], references: [id], onDelete: Cascade)
  
  @@index([userId])
  @@index([eventTypeId])
  @@index([startTime])
}

Purpose: Scheduled meetings between users and attendees.

Status Flow:

  1. pending - Initial booking state
  2. confirmed - User accepts
  3. cancelled - Either party cancels

Database Indexes

Optimized for common queries:

  • User lookups: email, username
  • Availability queries: userId, dayOfWeek
  • Booking searches: userId, eventTypeId, startTime

Authentication

NextAuth v5 Configuration

Provider: Credentials (email + password)

Flow:

1. User submits email/password
2. Server action validates with Prisma
3. bcrypt compares password hash
4. NextAuth creates session
5. Cookie stored (httpOnly, secure)

Implementation

File: auth.ts

export const { auth, signIn, signOut } = NextAuth({
  adapter: PrismaAdapter(prisma),
  session: { strategy: "jwt" },
  providers: [
    Credentials({
      credentials: {
        email: { type: "email" },
        password: { type: "password" }
      },
      authorize: async (credentials) => {
        // Validation with Zod
        // Prisma user lookup
        // bcrypt comparison
        // Return user or null
      }
    })
  ]
})

Session Management

  • Strategy: JWT (stateless)
  • Storage: HTTP-only cookies
  • Duration: 30 days (configurable)
  • Refresh: Automatic on activity

Security Features

  1. Password Hashing: bcrypt with salt rounds = 10
  2. CSRF Protection: Built into NextAuth
  3. Session Validation: Middleware protects routes
  4. SQL Injection: Prevented by Prisma ORM

API Routes

NextAuth Routes

Base: /api/auth/[...nextauth]

  • POST /api/auth/callback/credentials - Login
  • POST /api/auth/signout - Logout
  • GET /api/auth/session - Get session
  • GET /api/auth/csrf - CSRF token

Server Actions

Authentication

  • handleLogin(formData) - Login user
  • handleRegister(formData) - Create account
  • handleSignOut() - Sign out user

User Management

  • getUserProfile(userId) - Get user data
  • updateUserProfile(data) - Update profile

Development Setup

Prerequisites

  • Node.js 20.x
  • Docker & Docker Compose
  • npm/pnpm/yarn

Installation

# Clone repository
git clone git@github.com:edgarberlinck/event.me.git
cd event.me

# Install dependencies
npm install

# Setup environment
cp .env.example .env
# Edit .env with your values

# Start PostgreSQL
docker-compose up -d

# Run migrations
npx prisma db push

# Generate Prisma client
npx prisma generate

# Start development server
npm run dev

Environment Variables

# Database
DATABASE_URL="postgresql://postgres:postgres@localhost:55002/eventme"

# Authentication
NEXTAUTH_URL="http://localhost:3000"
NEXTAUTH_SECRET="your-secret-here"
AUTH_URL="http://localhost:3000"
AUTH_SECRET="your-secret-here"

Development Commands

npm run dev          # Start dev server
npm run build        # Production build
npm run lint         # Check code quality
npm run format       # Format code
npm test             # Run E2E tests
npm run test:ui      # Playwright UI mode
npm run db:studio    # Open Prisma Studio

Testing Strategy

E2E Tests (Playwright)

Location: tests/auth.spec.ts

Coverage:

  • User registration flow
  • Login/logout
  • Protected routes
  • Form validation
  • Error handling

Test Isolation:

  • Each test uses unique user email
  • Database state reset between runs
  • Parallel execution safe

Unit Tests (Vitest)

Location: __tests__/

Coverage:

  • Password hashing utilities
  • Form validation schemas
  • Component rendering

Running Tests

# E2E tests
npm test                    # All tests
npm run test:headed        # With browser UI
npm run test:debug         # Debug mode

# Unit tests
npm run test:unit          # Vitest tests

CI/CD Pipeline

GitHub Actions Workflow

File: .github/workflows/ci.yml

Jobs:

  1. Build (runs first)

    • Install dependencies
    • Generate Prisma client
    • Run database migrations
    • Build Next.js app
  2. Lint (parallel with E2E)

    • Run Biome checks
    • Verify code quality
  3. E2E (parallel with Lint)

    • Install Playwright browsers
    • Run full test suite
    • Upload test reports

Triggers:

  • Push to main
  • Pull requests to main
  • Manual dispatch

Pre-commit Hooks

File: .husky/pre-commit

Currently minimal (checks run in CI):

echo "✓ Pre-commit checks passed (lint and build run in CI)"

Deployment

  1. Vercel (Recommended)

    • Zero-config Next.js deployment
    • Automatic previews for PRs
    • Built-in PostgreSQL via Vercel Postgres
  2. Railway

    • PostgreSQL included
    • Simple deployment
  3. Self-hosted

    • Docker Compose setup included
    • Requires PostgreSQL instance

Environment Setup

Required Variables:

DATABASE_URL="postgresql://..."
NEXTAUTH_SECRET="generate-with-openssl"
NEXTAUTH_URL="https://your-domain.com"
AUTH_SECRET="same-as-nextauth-secret"
AUTH_URL="https://your-domain.com"

Build Command

npm run build

Start Command

npm start

Database Migrations

npx prisma migrate deploy

Performance Considerations

Database

  • Connection pooling via Prisma pg adapter
  • Proper indexes on frequently queried fields
  • Cascade deletes to maintain referential integrity

Next.js

  • Server components by default (reduced JS bundle)
  • Dynamic imports for heavy components
  • Image optimization with next/image

Caching

  • Static generation for public pages
  • ISR for semi-dynamic content
  • Database query caching (future enhancement)

Security Checklist

  • Password hashing with bcrypt
  • CSRF protection (NextAuth)
  • SQL injection prevention (Prisma)
  • XSS protection (React sanitization)
  • HTTP-only cookies
  • Environment variable validation
  • Protected API routes (middleware)
  • Rate limiting (future enhancement)
  • Email verification (future enhancement)

Monitoring & Logging

Current Setup

  • Next.js built-in error handling
  • Console logging in development
  • Sentry for error tracking
  • Vercel Analytics for performance
  • PostgreSQL slow query logs
  • Custom logging service (Winston/Pino)

Future Enhancements

Phase 2: Core Features

  • Availability management UI
  • Event type creation
  • Public booking pages
  • Calendar integration (Google, Outlook)

Phase 3: Advanced Features

  • Email notifications (Resend/SendGrid)
  • Time zone conversion
  • Custom booking questions
  • Payment integration (Stripe)

Phase 4: Scale & Polish

  • Multi-language support
  • Team scheduling
  • Analytics dashboard
  • Mobile app (React Native)

Troubleshooting

Common Issues

Database Connection Errors

# Check PostgreSQL is running
docker ps
# Restart if needed
docker-compose restart

Prisma Client Not Found

# Regenerate client
npx prisma generate

Port Already in Use

# Find and kill process on port 3000
lsof -ti:3000 | xargs kill -9

Migration Errors

# Reset database (CAUTION: deletes data)
npx prisma migrate reset

Contributing

Code Style

  • Follow Biome configuration
  • Use TypeScript strict mode
  • Write meaningful commit messages
  • Add tests for new features

Pull Request Process

  1. Fork repository
  2. Create feature branch
  3. Make changes with tests
  4. Run npm run lint and npm test
  5. Submit PR to main
  6. Wait for CI to pass
  7. Request review