
Data Fetching
Implement LobeHub data loading with Service layer, Zustand store SWR hooks, and lambdaClient instead of raw useEffect fetches.
Overview
data-fetching is an agent skill for the Build phase that implements LobeHub’s Service + Zustand + SWR + lambdaClient data-loading architecture.
Install
npx skills add https://github.com/lobehub/lobehub --skill data-fetchingWhat is this skill?
- Four-layer flow: Component → Zustand Store hook → Service → lambdaClient (TRPC)
- Mandates store SWR hooks for reads; discourages useEffect for data loading
- Write path: lambdaClient.mutate in services; lambdaClient.query only inside service methods
- Naming: useFetchXxx for reads, refreshXxx for cache invalidation after mutations
- Cross-references store-data-structures skill for List vs Detail Map/Array patterns
- Four-layer architecture: Component, Zustand Store, Service, lambdaClient
Adoption & trust: 811 installs on skills.sh; 78.4k GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
You are adding API-backed UI in LobeHub but reach for useEffect or direct TRPC calls in components, breaking caching and service boundaries.
Who is it for?
Developers extending LobeHub features who need the official fetch/mutate pattern with TRPC and Zustand.
Skip if: Greenfield apps outside LobeHub or projects using TanStack Query/RTK without adopting this stack.
When should I use this skill?
Implementing data fetching, creating services, working with store hooks, migrating from useEffect, or tasks involving data loading and API calls in LobeHub.
What do I get? / Deliverables
New reads flow through useFetchXxx store hooks and services, writes use lambdaClient.mutate, and refreshXxx helpers invalidate SWR after mutations.
- Service methods using lambdaClient.query/mutate
- Store useFetchXxx hooks and refreshXxx invalidation helpers
- Components wired through store hooks without useEffect fetching
Recommended Skills
Journey fit
How it compares
LobeHub-internal architecture skill—not a generic SWR or React Query starter.
Common Questions / FAQ
Who is data-fetching for?
Builders contributing to LobeHub who implement screens, services, and store hooks for TRPC-backed data.
When should I use data-fetching?
During Build when creating services, store SWR hooks, migrating off useEffect loads, or wiring lambdaClient mutate/query correctly.
Is data-fetching safe to install?
It describes patterns that imply API and filesystem work in your repo; review the Security Audits panel on this Prism page and follow your project's TRPC auth practices.
Workflow Chain
Requires first: store data structures
SKILL.md
READMESKILL.md - Data Fetching
# LobeHub Data Fetching Architecture > **Related:** `store-data-structures` covers List vs Detail data shape rationale (Map vs Array). ## Architecture Overview ```text ┌─────────────┐ │ Component │ └──────┬──────┘ │ 1. Call useFetchXxx hook from store ↓ ┌──────────────────┐ │ Zustand Store │ │ (State + Hook) │ └──────┬───────────┘ │ 2. useClientDataSWR calls service ↓ ┌──────────────────┐ │ Service Layer │ │ (xxxService) │ └──────┬───────────┘ │ 3. Call lambdaClient ↓ ┌──────────────────┐ │ lambdaClient │ │ (TRPC Client) │ └──────────────────┘ ``` ## Core Principles ### ✅ DO 1. **Use Service Layer** for all API calls 2. **Use Store SWR Hooks** for data fetching (not useEffect) 3. **Use proper data structures** — see `store-data-structures` skill for List vs Detail patterns 4. **Use lambdaClient.mutate** for write operations (create/update/delete) 5. **Use lambdaClient.query** only inside service methods 6. **Naming convention** — read hooks are `useFetchXxx`, cache invalidation helpers are `refreshXxx` (e.g. `useFetchBenchmarks` / `refreshBenchmarks`). Mutations then chain `refreshXxx()` after the service call. ### ❌ DON'T 1. **Never use useEffect** for data fetching 2. **Never call lambdaClient** directly in components or stores 3. **Never use useState** for server data 4. **Never mix data structure patterns** — follow `store-data-structures` skill --- ## Layer 1: Service Layer ### Purpose - Encapsulate all API calls to lambdaClient - Provide clean, typed interfaces - Single source of truth for API operations ### Service Structure ```typescript // src/services/agentEval.ts class AgentEvalService { // Query methods - READ operations async listBenchmarks() { return lambdaClient.agentEval.listBenchmarks.query(); } async getBenchmark(id: string) { return lambdaClient.agentEval.getBenchmark.query({ id }); } // Mutation methods - WRITE operations async createBenchmark(params: CreateBenchmarkParams) { return lambdaClient.agentEval.createBenchmark.mutate(params); } async updateBenchmark(params: UpdateBenchmarkParams) { return lambdaClient.agentEval.updateBenchmark.mutate(params); } async deleteBenchmark(id: string) { return lambdaClient.agentEval.deleteBenchmark.mutate({ id }); } } export const agentEvalService = new AgentEvalService(); ``` ### Service Guidelines 1. **One service per domain** (e.g., agentEval, ragEval, aiAgent) 2. **Export singleton instance** (`export const xxxService = new XxxService()`) 3. **Method names match operations** (list, get, create, update, delete) 4. **Clear parameter types** (use interfaces for complex params) --- ## Layer 2: Store with SWR Hooks ### Purpose - Manage client-side state - Provide SWR hooks for data fetching - Handle cache invalidation ### State Structure ```typescript // src/store/eval/slices/benchmark/initialState.ts export interface BenchmarkSliceState { // List data - simple array benchmarkList: AgentEvalBenchmarkListItem[]; benchmarkListInit: boolean; // Detail data - map for caching benchmarkDetailMap: Record<string, AgentEvalBenchmark>; loadingBenchmarkDetailIds: string[]; // Mutation states isCreatingBenchmark: boolean; isUpdatingBenchmark: boolean; isDeletingBenchmark: boolean; } ``` > For complete initialState, reducer, and internal dispatch patterns, see the `store-data-structures` skill. ### Actions ```typescript // src/store/eval/slices/benchmark/action.ts const FETCH_BENCHMARKS_KEY = 'FETCH_BENCHMARKS'; const FETCH_BENCHMARK_DETAIL_KEY = 'FETCH_BENCHMARK_DETAIL'; export interface Benchmar