Data fetching strategy
Server Components for initial data
Server Components fetch data directly on the server before rendering: Benefits:- No client-side loading states on initial render
- Better SEO (content in HTML)
- Reduced client bundle size
- Direct access to backend API
app/dashboard/components/DashboardContent.tsx:50-57:
- Use
Promise.all()to fetch data in parallel - No loading states needed (data ready before render)
- Pass data as props to child components
- Great for SEO and initial page load
TanStack Query for client-side data
Client Components use TanStack Query for interactive data: Benefits:- Automatic caching and revalidation
- Background refetching
- Optimistic updates
- Request deduplication
- Built-in loading/error states
app/dashboard/expenses/components/ExpensesListContent.tsx:
- Must be in a Client Component (
'use client') - Provides loading and error states
- Automatically caches results
- Revalidates on window focus (configurable)
API client architecture
Contafy has two API clients: one for Server Components and one for Client Components.Server API client
Location:lib/api/server-client.ts
Used in: Server Components only
Implementation:
- Reads cookies directly via
next/headers - Cannot refresh tokens (can’t modify cookies)
- Redirects to login on 401
- Throws errors for bad responses
Client API client
Location:lib/api/client.ts
Used in: Client Components only
Implementation:
- Uses
/backendproxy to avoid CORS - Automatically refreshes tokens on 401
- Retries failed requests with new token
- Redirects to login if refresh fails
Token refresh mechanism
Contafy implements automatic token refresh to keep users logged in.How it works
- User makes authenticated request
- Backend responds with 401 (token expired)
- Client automatically calls
/api/auth/refresh - Backend validates refresh token, returns new access token
- Client retries original request with new token
- User stays logged in without interruption
Implementation details
Refresh function (lib/api/client.ts:49-95):
- Prevents duplicate refresh requests with flag
- Shares single refresh promise across requests
- Redirects to login if refresh fails
- Transparent to calling code
Token storage
Tokens are stored in httpOnly cookies for security:- Access token: Short-lived (15 minutes)
- Refresh token: Long-lived (7 days)
- Cannot be accessed by JavaScript (XSS protection)
- Automatically sent with requests
- Secure flag in production (HTTPS only)
API proxy configuration
To avoid CORS issues, Contafy proxies API requests through Next.js. Configuration (next.config.ts:5-12):
- Client requests
/backend/api/invoices - Next.js rewrites to
http://localhost:3001/api/invoices - Backend processes request
- Next.js forwards response to client
- No CORS headers needed
- Simplified client code
- Works in all environments
TanStack Query configuration
Setup (components/providers/ReactQueryProvider.tsx:11-24):
Query keys
Query keys uniquely identify cached data:- Include all parameters that affect the data
- Use consistent ordering
- Use arrays for hierarchical keys
Query functions
Query functions fetch the actual data:- Must return a Promise
- Should use API client functions
- Can access query key parameters
Cache invalidation
Invalidate queries when data changes:Mutations
Mutations modify server data:Data flow examples
Server Component data flow
- User navigates to
/dashboard - Server Component renders
- Server fetches data via
serverApiClient - Server renders HTML with data
- Client receives fully rendered page
- No loading state on client
Client Component data flow
- User clicks “Ver más” button
- Client Component renders
- Shows loading spinner
- Client fetches data via
apiClient+ TanStack Query - Data cached by TanStack Query
- Component re-renders with data
- Cache reused on subsequent renders
Error handling
API errors
Both API clients throwApiError/ServerApiError: