Skip to main content

Data Flow

How data moves through the TrickBook application.

Authentication Flow

MongoDBBackendZustand StoreAppUserMongoDBBackendZustand StoreAppUserEnter email & passwordPOST /api/authFind user by emailUser documentbcrypt.compare(password)JWT token + user dataauthStore.login(token, user)SecureStore.setItem(token)Set user stateNavigate to (tabs) layout

App Startup Flow

// app/_layout.tsx - Simplified
const RootLayout = () => {
const { user, isLoading, loadStoredAuth } = useAuthStore();

useEffect(() => {
loadStoredAuth(); // Restore token from SecureStore
}, []);

if (isLoading) return <SplashScreen />;

return (
<QueryClientProvider client={queryClient}>
<ThemeProvider>
<Stack>
{user ? (
<Stack.Screen name="(tabs)" />
) : (
<Stack.Screen name="(auth)" />
)}
</Stack>
</ThemeProvider>
</QueryClientProvider>
);
};

Data Fetching with React Query

All API data flows through React Query for caching, deduplication, and automatic refetching.

// Example: Fetching trick lists
const { data: trickLists, isLoading, refetch } = useQuery({
queryKey: ['trickLists'],
queryFn: () => trickbookApi.getTrickLists(),
staleTime: 5 * 60 * 1000, // 5 minutes
});

// Example: Creating a trick list (mutation)
const createMutation = useMutation({
mutationFn: (data: CreateTrickListData) =>
trickbookApi.createTrickList(data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['trickLists'] });
},
});
React Query Cache Flow:

Component mounts → Check cache

├── Cache fresh → Return cached data

├── Cache stale → Return cached data + refetch in background

└── No cache → Fetch from API


apiClient.get('/listings')


Auto-inject x-auth-token header


Return data → Cache → Component

Trick List Data Flow

Creating a Trick List

1. User taps "+" button


2. Form with React Hook Form + Zod validation


3. useMutation → POST /api/listings
{ name: "Kickflips to learn" }


4. Backend creates in MongoDB
db.tricklists.insertOne({...})


5. Return { _id, name, tricks: [] }


6. Invalidate ['trickLists'] query → auto-refetch


7. Navigate to trick list detail screen

Tracking Trick Progress

PUT /api/listing/{trickId}
{
checked: "Landed" // "Not Started" | "Learning" | "Landed" | "Mastered"
}


Update in MongoDB


Invalidate query cache


Update StatusBadge + ProgressBar UI

Social Feed Data Flow

Creating a Post

Socket.IOMongoDBBunny.netAWS S3Backend APIAppUserSocket.IOMongoDBBunny.netAWS S3Backend APIAppUserRecord/select videoPOST /api/upload (video file)Upload to Bunny.net CDNVideo URL + thumbnailMedia URLsPOST /api/feed/postsInsert feed_postEmit new post eventReal-time feed update

Feed Algorithm Scoring

Feed posts are ranked by a scoring algorithm:

score = (engagement * 0.35) + (recency * 0.25) +
(completion * 0.25) + (interaction * 0.15)

Homie boost: Posts from friends get 2.5x multiplier
Recency: 48-hour half-life decay

Image Upload Flow

User selects image

expo-image-picker

expo-image-manipulator

Resize + compress

Create FormData

POST /api/image/upload

multer parses multipart

multer-s3 streams to S3

AWS S3

Return S3 URL

Update record in MongoDB

Display new image

Direct Messaging Flow

User BApp BMongoDBBackendSocket.IOApp AUser AUser BApp BMongoDBBackendSocket.IOApp AUser AType messageEmit typing indicatorShow "typing..."Send messagePOST /api/dm/messagesInsert dm_messageEmit new messageReal-time message deliveryShow new messageRead messagePUT /api/dm/messages/:id/read

Subscription Flow

MongoDBStripeBackend APIAppUserMongoDBStripeBackend APIAppUserplan: "premium"status: "active"Tap "Upgrade"POST /api/payments/create-checkout-sessionCreate checkout sessionSession URLRedirect URLOpen Stripe CheckoutComplete paymentWebhook: invoice.paidUpdate user.subscriptionNext API requestSubscription middleware checkPremium features enabled

Real-Time Data Flow

Socket.IO Connection Lifecycle:

App Launch


Connect to Socket.IO server

├── Auth: Pass JWT in handshake

├── /feed namespace
│ │
│ ├── Join room: user:{userId}
│ ├── Listen: 'post:update' → update React Query cache
│ ├── Listen: 'reaction:update' → update reaction counts
│ └── Listen: 'comment:new' → show notification

└── /messages namespace

├── Join room: user:{userId}
├── Listen: 'message:new' → add to conversation
├── Listen: 'typing' → show indicator
└── Listen: 'read' → update read receipts

API Request Pattern

All authenticated requests flow through a custom fetch client:

// src/lib/api/client.ts
const apiClient = {
get: async (url: string) => {
const token = await SecureStore.getItemAsync('authToken');
const response = await fetch(`${BASE_URL}${url}`, {
headers: {
'x-auth-token': token ?? '',
'Content-Type': 'application/json',
},
});
return response.json();
},
// post, put, delete follow same pattern
};

Features:

  • Auto-injects auth token from Secure Store
  • Retry logic (3 attempts with 1s delay)
  • Custom timeouts (30s default, 120s for uploads)
  • Environment-aware base URL (dev vs production)