Architecture Deep Dive: Technical Implementation Details - Part 1

Documentation
Guide

Architecture Deep Dive: Technical Implementation Details - Part 1

Comprehensive technical breakdown of TS Starter's architecture patterns, data flow, and implementation details

Documentation
Guide
0 Pages
Available

Core Architecture Patterns

1. Internal Services Architecture: SQLite DB → Drizzle → Better Auth → oRPC → TanStack Start

TS Starter's internal services follow a layered architecture pattern for secure, type-safe database operations. There are two distinct types of server-side routes:

  • TanStack Start Server Routes: All routes under src/routes/api/ that use createFileRoute
  • oRPC Server Routes: All routes under src/server/routes/ that define oRPC procedures

2. Step-by-Step Implementation

Step 1: Client Component Initiates Request

// src/routes/_authed/task.tsx
import { orpc } from "~/lib/orpc";
import { useMutation, useSuspenseQuery } from "@tanstack/react-query";

export function TaskComponent() {
  // INITIAL REQUEST: User clicks button to create task
  const handleCreateTask = async (taskData) => {
    // This is where the request actually starts!
    await createTaskMutation.mutateAsync({
      text: "Complete project documentation",
      priority: "high",
      hrs: 4,
      isPublic: false
    });
  };

  // INITIAL REQUEST: Component loads and fetches tasks
  const { data: tasks } = useSuspenseQuery({
    queryKey: ["tasks"] as const,
    queryFn: () => orpc.task.getAll.call(), // ← Request initiated here
    staleTime: 1000,
  });

  // INITIAL REQUEST: User submits form
  const createTaskMutation = useMutation({
    mutationFn: (data) => orpc.task.create.call(data), // ← Request initiated here
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ["tasks"] });
    },
  });

  return (
    <div>
      <button onClick={() => handleCreateTask()}>
        Create Task
      </button>
      
      {tasks?.map(task => (
        <div key={task.id}>{task.text}</div>
      ))}
    </div>
  );
}

Step 2: oRPC Client Handles Request

// src/lib/orpc.ts
const getORPCClient = createIsomorphicFn()
  .server(() =>
    createRouterClient(serverRouter, {
      context: async () => {
        const req = getWebRequest();
        // Use the server oRPC context to include the authenticated session
        const ctx = await createServerOrpcContext({
          headers: req.headers,
          req,
        });
        return ctx;
      },
    }),
  )
  .client((): RouterClient<typeof serverRouter> => {
    const link = new RPCLink({
      url: new URL("/api/rpc", import.meta.env.VITE_BASE_URL || window.location.origin),
      fetch: (input, init) =>
        fetch(input, {
          ...init,
          credentials: "include",
        }),
    });

    return createORPCClient(link);
  });

export const client: RouterClient<typeof serverRouter> = getORPCClient();

export const orpc = createORPCReactQueryUtils(client);

Step 3: TanStack Start Server Route Receives oRPC Request

// src/routes/api/rpc.$.ts
import { createFileRoute } from "@tanstack/react-router";
import { serverHandler } from "~/server/handler";
import { auth } from "~/lib/auth";

async function handle({ request }: { request: Request }) {
  // Get session data from the request headers
  const session = await auth.api.getSession({
    headers: request.headers,
    query: {
      disableCookieCache: true,
    },
  });

  const { response } = await serverHandler.handle(request, {
    context: {
      session, // This matches the expected { session: {...}, user: {...} } | null structure
    },
    prefix: "/api/rpc",
  });

  return response ?? new Response("Not Found", { status: 404 });
}

export const Route = createFileRoute("/api/rpc/$")({
  server: {
    handlers: {
      DELETE: handle,
      GET: handle,
      HEAD: handle,
      PATCH: handle,
      POST: handle,
      PUT: handle,
    },
  },
});

// src/server/handler.ts - This creates the serverHandler that handle() calls
import { RPCHandler, CompressionPlugin } from "@orpc/server/fetch";
import { CORSPlugin } from "@orpc/server/plugins";
import { serverRouter } from "./routes";

export const serverHandler = new RPCHandler(serverRouter, {
  plugins: [new CORSPlugin(), new CompressionPlugin()],
});

**3.1. The Connection Chain Explained

The handle function in rpc.$.ts connects to serverHandler through this flow:

rpc.$.ts (handle function)
    ↓
    calls serverHandler.handle()
    ↓
serverHandler (from ~/server/handler)
    ↓
    is RPCHandler with plugins
    ↓
serverRouter (from ~/server/routes)
    ↓
    contains all your oRPC procedures

How it works:

  1. HTTP Request arrives at /api/rpc/*
  2. TanStack Start routes to rpc.$.ts
  3. handle() function calls serverHandler.handle(request, context)
  4. serverHandler processes the request through oRPC procedures
  5. Response flows back through the chain

Step 4: oRPC Procedure with Authentication

// src/server/orpc.ts
import { auth } from "~/lib/auth";

// Create context with Better Auth session
export const createContext = async (opts: { headers: Headers; req: Request }) => {
  let session = null;
  try {
    // Better Auth provides the session
    session = await auth.api.getSession({ headers: opts.headers });
  } catch (error) {
    console.warn("Auth failed (SSR-safe):", error.message);
  }
  return { session };
};

// Protected procedures require authentication
const requireAuth = o.middleware(async ({ context, next }) => {
  if (!context.session?.user) {
    // Graceful fallback during SSR
    return next({ context: { session: context.session } });
  }
  return next({ context: { session: context.session } });
});

export const protectedProcedure = publicProcedure.use(requireAuth);

Step 5: Business Logic with Database Access

// src/server/routes/task.ts
import { protectedProcedure, taskPublisher } from "../orpc";
import { db } from "~/db";
import { tasks } from "~/db/schema";
import z from "zod";

export const taskRouter = {
  create: protectedProcedure
    .input(z.object({ 
      text: z.string(), 
      details: z.string().optional(),
      priority: z.enum(["high", "medium", "low"]).default("medium"),
      hrs: z.number().min(1).max(8).default(1)
    }))
    .handler(async ({ input, context }) => {
      // context.session.user is guaranteed to exist here
      const task = await db.insert(tasks).values({
        id: crypto.randomUUID(),
        text: input.text,
        details: input.details || "",
        priority: input.priority,
        hrs: input.hrs,
        status: "Not Started",
        userId: context.session.user.id, // Secure user association
        createdAt: new Date(),
        updatedAt: new Date(),
      }).returning();

      // Publish real-time event
      taskPublisher.publish("task-created", {
        taskId: task[0].id,
        userId: context.session.user.id,
        task: task[0],
      });

      return task[0];
    }),

  getAll: protectedProcedure.handler(async ({ context }) => {
    // Get user's own tasks
    const userTasks = await db.query.tasks.findMany({
      where: eq(tasks.userId, context.session.user.id),
    });

    // Get public tasks from other users
    const publicTasks = await db.query.tasks.findMany({
      where: and(
        eq(tasks.isPublic, true),
        ne(tasks.userId, context.session.user.id),
      ),
    });

    return { tasks: [...userTasks, ...publicTasks] };
  }),
};

Step 6: Drizzle ORM Executes SQLite Query

// src/db/schema.ts
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";

export const tasks = sqliteTable("tasks", {
  id: text("id").primaryKey(),
  text: text("text").notNull(),
  details: text("details"),
  priority: text("priority").notNull().default("medium"),
  status: text("status").notNull().default("Not Started"),
  hrs: integer("hrs").notNull().default(1),
  isPublic: integer("isPublic", { mode: "boolean" }).default(false),
  userId: text("userId").notNull().references(() => users.id),
  createdAt: integer("createdAt", { mode: "timestamp" }).notNull(),
  updatedAt: integer("updatedAt", { mode: "timestamp" }).notNull(),
  refLink: text("refLink"),
});

// src/db/index.ts
import { drizzle } from "drizzle-orm/libsql";
import { createClient } from "@libsql/client";

const client = createClient({
  url: env.DATABASE_URL,        // SQLite file or Turso URL
  authToken: env.DATABASE_AUTH_TOKEN,
});

export const db = drizzle(client, { schema });

Step 7: Response Flows Back Through the Chain

// Data flows back: Database → Drizzle → oRPC → Client → React Query → UI

// 1. Database returns result
const task = await db.insert(tasks).values({...}).returning();

// 2. oRPC procedure returns data
return task[0];

// 3. Client receives response through oRPC
const result = await orpc.task.create.call({ 
  text: "New task", 
  priority: "high",
  hrs: 2,
  isPublic: false
});

// 4. React Query caches the result and invalidates queries
const createTaskMutation = useMutation({
  mutationFn: (data) => orpc.task.create.call(data),
  onSuccess: () => {
    queryClient.invalidateQueries({ queryKey: ["tasks"] });
    createTaskForm.reset();
  },
});

// 5. UI updates automatically with real-time updates
const { data: tasks } = useSuspenseQuery({
  queryKey: ["tasks"] as const,
  queryFn: () => orpc.task.getAll.call(),
  staleTime: 1000,
  refetchOnWindowFocus: true,
  refetchOnMount: true,
});

// 6. Real-time updates via useTaskUpdates hook
useTaskUpdates((event: RealtimeTaskEvent) => {
  switch (event.type) {
    case "task-created":
      toast.success(`New task created: "${event.task?.text}"! 🎉`);
      queryClient.invalidateQueries({ queryKey: ["tasks"] });
      break;
    case "task-updated":
      toast.info(`Task updated: "${event.task?.text}"`);
      queryClient.invalidateQueries({ queryKey: ["tasks"] });
      break;
  }
});

🎯 Part 1 Complete: Core Internal Services Architecture

Congratulations! You've now completed Part 1 of our architecture deep-dive series. In this part, we've covered:

What We've Learned

  • Complete Internal Services Flow: From client component to database
  • oRPC Integration: How TanStack Start and oRPC work together

🚀 What's Next in the Series?

Part 2: Data Flow & State Management

  • React Query + oRPC integration patterns
  • Real-time updates with Event Publisher
  • Caching strategies and optimization

Part 3: Authentication & Security

  • Better Auth configuration details
  • CSRF protection implementation
  • User data isolation patterns

Part 4: External Service Integration

  • Plunk email service architecture
  • Polar payment webhook processing
  • Third-party API patterns

This is Part 1 of the TS Starter Architecture Deep-Dive series. Each part builds on the previous one to give you a complete understanding of the system.