Full-Stack Data Flow
Relevant source files
The following files were used as context for generating this wiki page:
Introduction
The Adfluens application employs a modern, type-safe, full-stack data flow architecture built within a Turborepo monorepo. This system facilitates seamless communication between the React frontend (apps/web) and the Express backend (apps/api). The core of this architecture is tRPC, which enables the frontend to call backend procedures as if they were local, type-safe functions, eliminating the need for manual API endpoint definition and client-side data fetching boilerplate.
This data flow is further enhanced by TanStack Query on the client for state management, caching, and data synchronization, and a clear separation of concerns on the server using a service-based pattern. A shared @repo/types package ensures that data structures are consistent across the entire stack, from the database to the UI components.
Sources: README.md:5-40, apps/web/package.json:14-16, apps/api/package.json:20-23
High-Level Architecture
The diagram below illustrates the overall data flow from the user's browser to the backend services and external APIs.
This diagram shows a request originating from a React component, flowing through tRPC and the backend services, and returning with data from external APIs.
Sources: README.md:43-50, apps/api/src/index.ts:35-42, apps/web/src/features/ai/components/video-analyzer.component.tsx:78-80
Frontend: Client-Side Data Fetching
The frontend application, located in apps/web, initiates all data requests. It uses a combination of tRPC's client library and TanStack Query to manage API communication and server state.
tRPC Client Setup
A singleton trpcClient is created and configured to communicate with the backend API. This client is aware of all the procedures available on the server, providing full type-safety and autocompletion in the frontend codebase.
The client is configured with httpBatchLink to batch multiple tRPC calls into a single HTTP request, optimizing network performance.
// File: apps/web/src/lib/trpc.ts
import { createTRPCReact } from "@trpc/react-query";
import { httpBatchLink } from "@trpc/client";
import type { AppRouter } from "@repo/api/router";
// Create a tRPC hook for React Query
export const trpc = createTRPCReact<AppRouter>();
// Create a tRPC client
export const trpcClient = trpc.createClient({
links: [
httpBatchLink({
url: `${import.meta.env.VITE_API_URL}/trpc`,
}),
],
});
Sources: apps/web/src/lib/trpc.ts
Component-Level Data Access
React components use the trpcClient directly to call backend procedures. For example, the VideoAnalyzer component calls the ai.analyze and ai.chat mutations to perform AI analysis.
This example shows how a mutate function is called from an event handler. The inputs and outputs of trpcClient.ai.analyze.mutate are fully typed based on the backend router's definition.
// File: apps/web/src/features/ai/components/video-analyzer.component.tsx:78-81
const handleAnalyze = async (force = false) => {
// ...
try {
const result = await trpcClient.ai.analyze.mutate({ video, comments });
setAnalysis(result);
// ...
};
Sources: apps/web/src/features/ai/components/video-analyzer.component.tsx:78-81
tRPC: The API Layer
tRPC serves as the strongly-typed bridge between the frontend and backend. It is not a traditional REST or GraphQL API; instead, it's a library for building and consuming type-safe APIs.
Server-Side Router (appRouter)
The backend defines a main appRouter that merges smaller, feature-specific routers. This follows the "Feature-Based Organization" principle outlined in the project's architecture. Each feature router, like aiRouter or youtubeRouter, is responsible for its own set of procedures.
// File: apps/api/src/trpc/router.ts
import { t } from "./init";
import { aiRouter } from "../features/ai/ai.router";
import { youtubeRouter } from "../features/youtube/youtube.router";
export const appRouter = t.router({
ai: aiRouter,
youtube: youtubeRouter,
});
export type AppRouter = typeof appRouter;
Sources: apps/api/src/trpc/router.ts, README.md:43-50
This modular router structure is visualized below:
This diagram shows how the main appRouter delegates requests to feature-specific routers based on the procedure path.
Sources: apps/api/src/trpc/router.ts:6-9
Express Middleware
The appRouter is exposed as an Express middleware on the /trpc path. This allows the tRPC client from the frontend to send HTTP requests to the backend.
// File: apps/api/src/index.ts:35-42
// tRPC API with authentication context
app.use(
"/trpc",
trpcExpress.createExpressMiddleware({
router: appRouter,
createContext,
})
);
Sources: apps/api/src/index.ts:35-42
Backend: Service-Oriented Logic
The backend follows a "Server Services Pattern" where the tRPC routers (*.router.ts) are thin layers that delegate business logic to service files (*.service.ts). This separates API definitions from their implementation.
Service Implementation
Service files contain the core business logic and interact with external APIs or databases. For instance, ai.service.ts contains the analyzeVideo function, which constructs a prompt and calls the Google Gemini AI API.
// File: apps/api/src/features/ai/ai.service.ts:12-16
export const analyzeVideo = async (
video: YouTubeVideo,
comments: YouTubeComment[]
): Promise<ViralAnalysisResult> => {
const ai = new GoogleGenAI({ apiKey: env.GEMINI_API_KEY });
// ... prompt construction ...
const response = await ai.models.generateContent({
model: GEMINI_MODEL,
contents: prompt,
config: {
tools: [{ googleSearch: {} }],
},
});
// ...
return result;
};
Sources: apps/api/src/features/ai/ai.service.ts:12-46, README.md:52-54
Data Flow Sequence: Video Analysis
The following sequence diagram illustrates the full-stack data flow for the "Analyze Video" feature.
This diagram shows the journey of a request from the React component to the Gemini API and back.
Sources: apps/web/src/features/ai/components/video-analyzer.component.tsx:78-81, apps/api/src/trpc/router.ts:7, apps/api/src/features/ai/ai.service.ts:12-46
Shared Types (@repo/types)
A key element of this architecture is the @repo/types package. It centralizes all shared TypeScript types and Zod schemas, ensuring type consistency between the frontend and backend.
This package exports types for different domains, such as youtube and ai. The package.json file defines explicit export paths for each type, allowing other packages in the monorepo to import them directly.
// File: packages/types/package.json (exports section)
"exports": {
"./youtube/youtube-video.type": {
"import": "./src/youtube/youtube-video.type.ts",
"types": "./src/youtube/youtube-video.type.ts"
},
"./ai/viral-analysis-result.type": {
"import": "./src/ai/viral-analysis-result.type.ts",
"types": "./src/ai/viral-analysis-result.type.ts"
},
"./ai/chat-message.type": {
"import": "./src/ai/chat-message.type.ts",
"types": "./src/ai/chat-message.type.ts"
}
}
Sources: packages/types/package.json:5-34
Both the frontend components (e.g., VideoAnalyzer) and backend services (e.g., ai.service.ts) import and use these shared types, guaranteeing that the data shape is identical on both ends of the wire.
// Frontend usage
// File: apps/web/src/features/ai/components/video-analyzer.component.tsx:3-5
import type { YouTubeVideo } from "@repo/types/youtube/youtube-video.type";
import type { YouTubeComment } from "@repo/types/youtube/youtube-comment.type";
import type { ViralAnalysisResult } from "@repo/types/ai/viral-analysis-result.type";
// Backend usage
// File: apps/api/src/features/ai/ai.service.ts:2-4
import type { YouTubeVideo } from "@repo/types/youtube/youtube-video.type";
import type { YouTubeComment } from "@repo/types/youtube/youtube-comment.type";
import type { ViralAnalysisResult } from "@repo/types/ai/viral-analysis-result.type";
Sources: apps/web/src/features/ai/components/video-analyzer.component.tsx:3-5, apps/api/src/features/ai/ai.service.ts:2-4
Summary
The full-stack data flow in Adfluens is a robust, modern system centered around tRPC for type-safe API communication. By leveraging a monorepo, shared type definitions, and a clear separation of concerns between client, API layer, and server-side logic, the architecture achieves high developer productivity and runtime safety. This setup minimizes boilerplate, prevents data shape mismatches, and provides a seamless development experience across the entire application stack.