Eventuall Documentation

Last Updated: 2025-09-08

What you'll learn

By reading this documentation, you'll understand:

  • How to build modern real-time web applications with Next.js 15 and React 19
  • Type-safe API development with tRPC and end-to-end type safety
  • Server-side rendering patterns with React Server Components
  • Real-time features using LiveKit, Twilio, and WebSockets
  • Edge computing with Cloudflare Workers and Durable Objects
  • Modern authentication patterns with NextAuth
  • Database design and migrations with Drizzle ORM
  • UI development with shadcn/ui and Tailwind CSS
  • Infrastructure as Code with Terraform

Introduction

Welcome to Eventuall - a production-ready, real-time event platform that demonstrates modern web development best practices. Whether you're a junior developer learning these technologies or a mid-level developer looking to understand advanced patterns, this documentation will guide you through every aspect of the codebase.

Why Eventuall?

Traditional event platforms often struggle with real-time interactions, scalability, and user experience. Eventuall solves these challenges by leveraging:

  • Edge Computing: Runs at the edge for minimal latency
  • Type Safety: End-to-end type safety prevents runtime errors
  • Real-time First: Built for live interactions from the ground up
  • Modern Stack: Uses the latest stable versions of all technologies

Prerequisites

Before diving into the codebase, you should be familiar with:

  • JavaScript/TypeScript fundamentals - Basic syntax and concepts
  • React basics - Components, props, and state
  • Git version control - Basic commands and workflows
  • Command line usage - Terminal/shell basics

Good to know: If you're new to any of these technologies, we recommend completing their official tutorials first. This documentation assumes basic familiarity but will explain advanced concepts in detail.

Getting Started

There are multiple ways to explore the Eventuall codebase depending on your learning style and goals:

Learning Paths

For Beginners: Start with the Fundamentals

  1. Quick Start Guide - Get the application running locally
  2. Architecture Overview - Understand the system design
  3. UI Components and Styling - Learn the component system
  4. Database Architecture - Master data management

For Intermediate Developers: Deep Dive into Features

  1. Frontend Architecture - Next.js 15 App Router and Server Components
  2. tRPC and Server Actions - Type-safe API development and form handling
  3. Authentication - NextAuth, OAuth, OTP, and RBAC
  4. Real-Time Architecture - LiveKit, Twilio, and WebSocket integration

For Architects: System Design and Patterns

  1. Architecture Overview - High-level design decisions
  2. Backend Architecture - Workers, Durable Objects, and queue systems
  3. Compositor and Recording - Recording pipeline and video orchestration
  4. Infrastructure and Deployment - Terraform, environments, and tunnels

Interactive Learning

The best way to learn is by doing. Try these exercises:

// Exercise 1: Create your first Server Component
// File: apps/webapp/src/app/practice/page.tsx

export default async function PracticePage() {
  // TODO: Fetch data from the database
  // TODO: Render the data
  // Hint: Check how other pages fetch data
  
  return (
    <div>
      <h1>Your First Server Component</h1>
      {/* Your implementation here */}
    </div>
  );
}

Project Structure

Understanding the project structure is crucial for navigating the codebase effectively:

eventuall-app/
├── apps/                     # Application workspaces
│   ├── webapp/              # Next.js frontend application
│   │   ├── src/
│   │   │   ├── app/        # App Router pages and layouts
│   │   │   ├── components/ # React components
│   │   │   ├── server/     # Server-side logic
│   │   │   └── trpc/       # tRPC configuration
│   │   └── public/         # Static assets
│   └── workers/            # Cloudflare Workers backend
│       └── src/
│           ├── durable-objects/ # Stateful workers
│           └── routes/     # API routes
├── packages/               # Shared packages
├── terraform/             # Infrastructure as Code
│   ├── modules/          # Reusable Terraform modules
│   └── environments/     # Environment configurations
├── scripts/              # Utility scripts
└── docs/                # Documentation (you are here!)

Key Directories Explained

`apps/webapp/src/app/` - The Heart of the Application

This directory contains all your pages, layouts, and routing logic using Next.js 15's App Router:

// Example: apps/webapp/src/app/event/[eventId]/page.tsx
export default async function EventPage(props: {
  params: Promise<{ eventId: string }>;
}) {
  const params = await props.params;
  // Page implementation
}

`apps/webapp/src/components/` - Reusable UI Components

Organized by feature and complexity:

components/
├── ui/           # Base UI components (buttons, cards, etc.)
├── forms/        # Form components with validation
├── layouts/      # Layout components
└── features/     # Feature-specific components

`apps/webapp/src/server/` - Server-Side Logic

Contains all server-side code including:

  • api/ - tRPC routers and procedures
  • actions/ - Server Actions for forms
  • db/ - Database schema and utilities
  • auth/ - Authentication configuration

Core Concepts

1. Server Components vs Client Components

One of the most important concepts in Next.js 15 is the distinction between Server and Client Components:

// Server Component (default) - Runs on the server
// File: apps/webapp/src/app/products/page.tsx
import { db } from "@/server/db";

export default async function ProductsPage() {
  // Direct database access - only possible in Server Components
  const products = await db.query.products.findMany();
  
  return (
    <div>
      {products.map(product => (
        <ProductCard key={product.id} product={product} />
      ))}
    </div>
  );
}

// Client Component - Runs in the browser
// File: apps/webapp/src/components/interactive-filter.tsx
"use client"; // This directive makes it a Client Component

import { useState } from "react";

export function InteractiveFilter() {
  // State and event handlers - only possible in Client Components
  const [filter, setFilter] = useState("");
  
  return (
    <input 
      value={filter}
      onChange={(e) => setFilter(e.target.value)}
      placeholder="Filter products..."
    />
  );
}

When to Use Each

Server Components Client Components
Data fetching Interactive UI
Backend access Event handlers
Large dependencies Browser APIs
SEO-critical content Real-time updates

Good to know: Components are Server Components by default. Only add "use client" when you need client-side features.

2. Type-Safe APIs with tRPC

tRPC provides end-to-end type safety without code generation:

// Define a router with procedures
// File: apps/webapp/src/server/api/routers/product.router.ts
import { z } from "zod";
import { createTRPCRouter, protectedProcedure } from "@/server/api/trpc";

export const productRouter = createTRPCRouter({
  // Input validation with Zod
  create: protectedProcedure
    .input(z.object({
      name: z.string().min(1),
      price: z.number().positive(),
    }))
    .mutation(async ({ input, ctx }) => {
      // Type-safe input and context
      const product = await ctx.db.insert(products).values({
        name: input.name,    // TypeScript knows this is a string
        price: input.price,  // TypeScript knows this is a number
        userId: ctx.user.id, // Context provides authenticated user
      });
      
      return product;
    }),
});

// Use in a component with full type safety
// File: apps/webapp/src/app/products/create-form.tsx
"use client";
import { api } from "@/trpc/client";

export function CreateProductForm() {
  const createProduct = api.product.create.useMutation();
  
  const handleSubmit = () => {
    createProduct.mutate({
      name: "New Product",
      price: 99.99,
      // TypeScript error if you try: price: "invalid"
    });
  };
}

3. Modern Form Handling

Forms can be handled in multiple ways, each with its own benefits:

// Server Action with validation
// File: apps/webapp/src/server/actions/product.ts
"use server";

import { authenticatedActionClient } from "@/server/safe-action";
import { z } from "zod";

const schema = z.object({
  name: z.string().min(3, "Name must be at least 3 characters"),
  price: z.number().positive("Price must be positive"),
});

export const createProductAction = authenticatedActionClient
  .schema(schema)
  .action(async ({ parsedInput, ctx }) => {
    // Validated input with proper types
    const product = await ctx.db.insert(products).values({
      ...parsedInput,
      userId: ctx.user.id,
    });
    
    return { success: true, product };
  });

// Use in a form component
// File: apps/webapp/src/components/product-form.tsx
"use client";
import { useAction } from "next-safe-action/hooks";

export function ProductForm() {
  const { execute, result, isExecuting } = useAction(createProductAction);
  
  return (
    <form action={execute}>
      <input name="name" required />
      <input name="price" type="number" required />
      <button disabled={isExecuting}>
        {isExecuting ? "Creating..." : "Create Product"}
      </button>
      {result?.data?.error && <p>{result.data.error}</p>}
    </form>
  );
}

Technology Deep Dives

Frontend Technologies

Next.js 15 with App Router

The App Router introduces several powerful concepts:

  • Nested Layouts - Share UI between routes
  • Parallel Routes - Render multiple pages simultaneously
  • Intercepting Routes - Modal-like experiences
  • Loading States - Built-in loading UI
  • Error Boundaries - Graceful error handling

Learn more about Frontend Architecture →

React 19 Features

We leverage the latest React features:

  • Server Components - Reduce client bundle size
  • Suspense - Progressive loading
  • Concurrent Features - Better performance
  • Improved Hydration - Faster interactivity

Tailwind CSS v3 + shadcn/ui

Our styling solution combines:

  • Utility-first CSS - Rapid development
  • Component variants - Consistent design
  • Dark mode support - Built-in theming
  • Accessibility - ARIA-compliant components

Learn more about UI Components →

Backend Technologies

Cloudflare Workers

Edge computing provides:

  • Global distribution - Run code close to users
  • Auto-scaling - Handle any load
  • Zero cold starts - Instant responses
  • Integrated services - D1, R2, KV, Queues

Drizzle ORM

Type-safe database access with:

  • SQL-like syntax - Familiar for SQL users
  • Type inference - Automatic TypeScript types
  • Migration system - Version control for schemas
  • Multiple drivers - Works with many databases

Learn more about Database Architecture →

Real-time Technologies

LiveKit

WebRTC infrastructure for:

  • Video calls - High-quality video/audio
  • Screen sharing - Collaborative features
  • Recording - Save sessions
  • Scalability - Handles thousands of participants

Twilio

Messaging platform for:

  • SMS - Text notifications
  • Conversations - Chat functionality
  • Voice - Phone integration
  • Video - Alternative video solution

Learn more about Real-Time Architecture →

Common Patterns

Data Fetching Patterns

// Pattern 1: Fetch in Server Component
export default async function Page() {
  const data = await api.product.list();
  return <ProductList products={data} />;
}

// Pattern 2: Prefetch for Client Component
export default async function Page() {
  // Prefetch data for hydration
  void api.product.list.prefetch();
  
  return (
    <HydrateClient>
      <ClientProductList />
    </HydrateClient>
  );
}

// Pattern 3: Streaming with Suspense
export default function Page() {
  return (
    <Suspense fallback={<ProductsSkeleton />}>
      <Products />
    </Suspense>
  );
}

Error Handling Patterns

// Pattern 1: Try-Catch in Server Components
export default async function Page() {
  try {
    const data = await riskyOperation();
    return <Success data={data} />;
  } catch (error) {
    return <ErrorMessage error={error} />;
  }
}

// Pattern 2: Error Boundaries
// File: app/error.tsx
"use client";
export default function Error({
  error,
  reset,
}: {
  error: Error;
  reset: () => void;
}) {
  return (
    <div>
      <h2>Something went wrong!</h2>
      <button onClick={reset}>Try again</button>
    </div>
  );
}

// Pattern 3: tRPC Error Handling
const mutation = api.product.create.useMutation({
  onError: (error) => {
    if (error.data?.code === 'UNAUTHORIZED') {
      router.push('/login');
    }
  },
});

Performance Best Practices

1. Optimize Bundle Size

// Use dynamic imports for large components
const HeavyChart = dynamic(() => import('@/components/heavy-chart'), {
  loading: () => <ChartSkeleton />,
  ssr: false, // Disable SSR for client-only components
});

2. Image Optimization

import Image from 'next/image';

// Next.js automatically optimizes images
<Image
  src="/hero.jpg"
  alt="Hero image"
  width={1200}
  height={600}
  priority // Load immediately for above-the-fold images
  quality={85} // Balance quality vs file size
/>

3. Data Caching

// Cache expensive operations
import { unstable_cache } from 'next/cache';

const getCachedProducts = unstable_cache(
  async () => {
    return db.query.products.findMany();
  },
  ['products'], // Cache key
  {
    revalidate: 60, // Revalidate every 60 seconds
    tags: ['products'], // For on-demand revalidation
  }
);

Security Considerations

Authentication

We use NextAuth for secure authentication:

// Protect pages with middleware
// File: middleware.ts
import { auth } from "@/server/auth";

export default auth((req) => {
  if (!req.auth && req.nextUrl.pathname.startsWith("/dashboard")) {
    return Response.redirect(new URL("/login", req.url));
  }
});

export const config = {
  matcher: ["/dashboard/:path*"],
};

Input Validation

Always validate user input:

// Validate with Zod schemas
const schema = z.object({
  email: z.string().email(),
  age: z.number().min(18).max(120),
  website: z.string().url().optional(),
});

// Automatic validation in tRPC
.input(schema)
.mutation(async ({ input }) => {
  // input is validated and typed
});

SQL Injection Prevention

Drizzle ORM prevents SQL injection:

// Safe: Uses parameterized queries
await db.query.users.findFirst({
  where: eq(users.email, userInput),
});

// Never do this:
// await db.execute(`SELECT * FROM users WHERE email = '${userInput}'`);

Testing Strategies

Unit Testing

// Test individual functions
// File: __tests__/utils.test.ts
import { describe, it, expect } from 'vitest';
import { calculatePrice } from '@/lib/utils';

describe('calculatePrice', () => {
  it('applies discount correctly', () => {
    expect(calculatePrice(100, 0.2)).toBe(80);
  });
  
  it('handles edge cases', () => {
    expect(calculatePrice(0, 0.5)).toBe(0);
    expect(calculatePrice(100, 0)).toBe(100);
  });
});

Integration Testing

// Test API endpoints
// File: __tests__/api.test.ts
import { createCaller } from '@/server/api/root';

describe('Product API', () => {
  it('creates a product', async () => {
    const caller = createCaller({ user: mockUser });
    
    const product = await caller.product.create({
      name: 'Test Product',
      price: 99.99,
    });
    
    expect(product).toHaveProperty('id');
    expect(product.name).toBe('Test Product');
  });
});

E2E Testing

// Test user workflows
// File: e2e/product-flow.test.ts
import { test, expect } from '@playwright/test';

test('user can create and view product', async ({ page }) => {
  await page.goto('/products/new');
  await page.fill('[name="name"]', 'New Product');
  await page.fill('[name="price"]', '49.99');
  await page.click('button[type="submit"]');
  
  await expect(page).toHaveURL(/\/products\/\w+/);
  await expect(page.locator('h1')).toContainText('New Product');
});

Troubleshooting Guide

Common Issues and Solutions

❌ "Module not found" errors

Problem: TypeScript can't find your imports

Solution:

# Clear build cache
rm -rf .next

# Reinstall dependencies
pnpm install

# Restart TypeScript server in VS Code
Cmd+Shift+P → "TypeScript: Restart TS Server"
❌ "Session is null" in protected routes

Problem: Authentication isn't working

Solution:

# Check environment variables
echo $NEXTAUTH_SECRET # Should not be empty

# Generate a new secret if needed
openssl rand -base64 32
❌ Database migration errors

Problem: Migrations won't apply

Solution:

# Regenerate migrations
pnpm generate

# Apply locally
pnpm migrate:local

# Check migration files
ls apps/webapp/drizzle/

Development Workflow

Daily Development Flow

graph LR
    A[Pull Latest] --> B[Create Branch]
    B --> C[Write Code]
    C --> D[Test Locally]
    D --> E[Run Linters]
    E --> F[Commit]
    F --> G[Push & PR]
    G --> H[Code Review]
    H --> I[Merge]

Best Practices Checklist

Before submitting a PR, ensure:

  • ✅ All tests pass (pnpm test)
  • ✅ No TypeScript errors (pnpm check-types)
  • ✅ Code is formatted (pnpm format)
  • ✅ No linting errors (pnpm lint)
  • ✅ Documentation updated if needed
  • ✅ Migrations generated for schema changes
  • ✅ Environment variables documented

Quick Reference

Essential Commands

# Development
pnpm dev                # Start dev server
pnpm build             # Build for production
pnpm test              # Run tests
pnpm lint              # Check code quality

# Database
pnpm generate          # Generate migrations
pnpm migrate:local     # Apply migrations locally
pnpm studio            # Open Drizzle Studio

# Infrastructure
./scripts/setup.sh     # Setup infrastructure
terraform apply        # Apply changes
terraform destroy      # Cleanup

File Naming Conventions

page.tsx              # Pages
layout.tsx            # Layouts
loading.tsx           # Loading states
error.tsx             # Error boundaries
route.ts              # API routes
*.router.ts           # tRPC routers
*.action.ts           # Server Actions
*.schema.ts           # Zod schemas

Import Aliases

@/app         → apps/webapp/src/app
@/components  → apps/webapp/src/components
@/server      → apps/webapp/src/server
@/lib         → apps/webapp/src/lib
@/trpc        → apps/webapp/src/trpc

Getting Help

Resources

  • Internal Documentation: You're reading it!
  • Team Slack: #eventuall-dev channel
  • Code Search: Use VS Code's search (Cmd+Shift+F)
  • Git History: Check commit messages for context

External Documentation

Contributing

How to Contribute

  1. Find an Issue: Check the project board
  2. Discuss: Comment on the issue
  3. Implement: Follow the patterns in this guide
  4. Test: Write tests for your changes
  5. Document: Update relevant documentation
  6. Submit: Create a PR with clear description

Code Review Guidelines

When reviewing code, check for:

  • Correctness: Does it solve the problem?
  • Performance: Are there any bottlenecks?
  • Security: Are inputs validated?
  • Maintainability: Is it easy to understand?
  • Consistency: Does it follow project patterns?

Next Steps

Ready to dive deeper? Choose your path:

📚 Continue Learning

🛠️ Start Building

  1. Set up your development environment
  2. Create a practice branch
  3. Build a simple feature
  4. Submit your first PR

💡 Try These Exercises

  1. Create a new page with data fetching
  2. Add a tRPC endpoint with validation
  3. Build a form with Server Actions
  4. Style a component with Tailwind CSS

Welcome to the Eventuall team! 🎉

Remember: Good code is written once but read many times. Write for your future self and your teammates. When in doubt, refer to this documentation or ask for help. Happy coding!