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)