name: role-fullstack:api-frontend-integration description: | Integrate APIs with frontend applications using type-safe patterns: tRPC for end-to-end type safety, REST client generation (openapi-typescript, orval), TanStack Query / SWR for data fetching, optimistic updates, and error handling. allowed-tools: Read, Grep, Glob, Bash
API-Frontend Integration
When to Use
Activate when connecting a backend API to a frontend application, setting up data-fetching patterns, implementing optimistic updates, or establishing type-safe API contracts.
Integration Strategy Selection
| Approach | Type Safety | Setup Cost | Best For |
|---|---|---|---|
| tRPC | Full E2E | Low | Monorepos, same-team ownership |
| OpenAPI codegen | Generated | Medium | REST APIs, multi-team |
| GraphQL + codegen | Generated | Medium | Complex relational data queries |
| Manual fetch + Zod | Runtime | Low | Simple APIs, third-party services |
tRPC Integration (Recommended for Monorepos)
- Define routers in
packages/api/src/routers/with input validation via Zod. - Export the
AppRoutertype -- never the runtime object -- to the frontend. - Create a tRPC client with
httpBatchLinkorhttpSubscriptionLink. - Wrap the app with
trpc.ProviderandQueryClientProvider. - Use
trpc.useQuery()andtrpc.useMutation()-- types flow automatically.
REST Client Generation
- openapi-typescript -- generates TypeScript types from OpenAPI 3.x specs. Pair with
openapi-fetchfor typed fetch calls. - orval -- generates React Query hooks, Zod schemas, and mock service workers from OpenAPI specs. Preferred for complex REST APIs.
- Automate generation in CI:
"codegen": "orval --config orval.config.ts".
Data Fetching Patterns (TanStack Query / SWR)
// Query with proper typing, stale time, and error handling
const { data, isLoading, error } = useQuery({
queryKey: ['users', filters],
queryFn: () => api.users.list(filters),
staleTime: 5 * 60 * 1000,
retry: 3,
retryDelay: (attempt) => Math.min(1000 * 2 ** attempt, 10000),
});
Optimistic Updates
- Use
onMutateto snapshot previous data and apply the optimistic change. - Use
onErrorto rollback to the snapshot on failure. - Use
onSettledto invalidate queries and refetch the authoritative state. - Show a subtle indicator (toast, inline badge) so the user knows the change is pending.
Error Handling Strategy
- API errors -- define a shared error shape (
{ code, message, details? }). Parse with Zod on the client. - Network errors -- distinguish between offline, timeout, and server errors. Show different UI for each.
- Error boundaries -- wrap route segments with React Error Boundaries. Provide a "Retry" action.
- Form validation -- validate on the client first (Zod + react-hook-form), then re-validate on the server. Surface server-side errors next to the relevant field.
Loading and Empty States
- Use skeleton loaders (not spinners) for content areas to reduce perceived latency.
- Always handle the empty state: "No results found" with a clear call-to-action.
- Show stale data with a background refresh indicator rather than a full loading state on refetch.
Common Pitfalls
- Fetching inside
useEffectwithout cancellation -- always use TanStack Query or SWR. - Forgetting to invalidate related queries after a mutation.
- Not deduplicating concurrent identical requests (TanStack Query handles this automatically).
- Ignoring error responses and only checking
data-- always handle the error path.