Skip to content
cccskills
FreePublished on GitHubEnglish overview

data-fetching-architecture

Guide client data fetching with SWR, a library for request state and caching. The skill helps place API calls in services and store hooks, handle loading and errors, refresh caches after writes, and migrate fetch logic out of component effects.

OriginalShow the original description

Use for client APIs, SWR hooks, cache invalidation, async errors, useEffect migration, home first paint and persistent caches.

lobehub/lobehub83,504 stars(GitHub 83,504 + cccskills 0)

Run score Not tested

See how to install

Good for

  • Move a component fetch into a store hook
  • Fix stale data after a write
  • Handle loading, empty, and error states

Overview

What it does

Guide the design of client-side data fetching across services, store hooks, and components. Use useClientDataSWR for reads and keep API calls in services. The guidance covers shared cache keys, refreshing data after writes, pending-state management, and distinguishing initial loading, empty results, and errors. It also helps move requests from useEffect and duplicated component state into store hooks and selectors. For lists and details rendered from local copies, it explains how to synchronize Replica data.

When it helps

Use it to investigate stale displays, requests that appear stuck loading, or initial errors that look like empty results. It can also help decide where fetching logic belongs.

Example requests

  • “Move this component’s fetch logic into a store SWR hook.”
  • “Find why the list stays stale after an update.”
  • “Show how to distinguish an initial error from an empty result.”

Notes

Implementations should follow LobeHub’s existing wrappers and code structure. Check the actual wrapper configuration instead of assuming SWR’s default behavior.

This overview was written by AI (OpenAI Codex) from the public SKILL.md. Check the original below for the exact behavior.

Document check

AI checked the SKILL.md for operations that need care and for setup requirements. The score comes only from actually running the skill (hands-on test).

SafetyAre sending data, deleting files, and running commands disclosed?
AGoodIt specifies rollback and deletion behavior for failed optimistic writes.
Easy setupDoes it need few tools or API keys, and are they documented?
BPartly lackingIt assumes SWR and LobeHub wrappers but provides no installation steps.

Not counted in the score. Rated by AI (OpenAI Codex) as of Oct 26, 2026. Treat it as a guide and read the original before installing.

Disagree? Request a re-evaluation

Tell us which point is wrong and why. No login needed. We will check it against the SKILL.md, redo the evaluation, and show the result here.

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

Files included (3)

  • SKILL.md7.0 KB
  • references/home-first-paint.md3.9 KB
  • references/walkthrough.md3.5 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

LobeHub Data Fetching Architecture

Component → Store useFetchXxx hook → Service → lambdaClient
          ← SWR request state + store data ← response

Layer boundaries

  • Services in src/services/ own API calls: .query() for reads, .mutate() for writes. Export a service instance per domain; components and stores do not call lambdaClient directly.
  • Store read hooks use useClientDataSWR and return its SWR response, including error and mutate. Sync successful results into the store through the wrapper's supported callback (onSuccess, or onData for the sync wrapper).
  • Server lists and details that paint from a local copy go through @/libs/replica instead: the store's useFetchXxx returns slice.useSync(params) (flags only) and the engine writes the store. See the Replica section of zustand.
  • Components call these hooks and read store data through selectors. Do not fetch in useEffect or duplicate server data in component useState.
  • Use useFetchXxx for read hooks and refreshXxx for cache invalidation.
  • For list/detail types, maps, reducers, and shared type sources, use Zustand data structures. For store layout and action classes, use zustand.

Home First Paint and Persistent Caches

When changing home/sidebar first paint or persisted display data, read references/home-first-paint.md. It covers avoiding flicker, reusing persistence, and the user displaySnapshot.ts boundary.

Cache keys and refresh

Reuse the domain key factory in src/libs/swr/keys.ts; define a shared key if the domain has none. Include every parameter that changes the response: entity or parent id, filters, sort, and pagination. Read and refresh must construct the same key.

Import useClientDataSWR and mutate from @/libs/swr, keeping the application's workspace/cache handling. Return a null key when a required id is absent; passing undefined to a store hook only disables fetching if that hook maps it to null.

For example, inside an action class:

useFetchBenchmarks = () =>
  useClientDataSWR(evalKeys.benchmarks(), () => agentEvalService.listBenchmarks(), {
    onSuccess: (data) => {
      this.#set({ benchmarkList: data, benchmarkListInit: true });
    },
  });

refreshBenchmarks = async () => {
  await mutate(evalKeys.benchmarks());
};

Read the current benchmark action for store wiring and the SWR wrappers for their actual options. These links locate implementation; they do not make every existing call site a template to copy unchanged.

Hooks that share a known parent id can run together. If one request needs a value returned by another, keep its key null until that value exists. Do not assume the application wrapper uses upstream SWR's default deduplication interval.

For lists cached separately under multiple parents, see parent-keyed lists. Ordinary flat lists need no extra layer.

Mutations

  • Call the service, then refresh affected list/detail keys and any related domain whose response changed. Await refresh when subsequent UI behavior depends on it.
  • Keep pending flags in store state and clear them in finally. Use per-id state for row updates/deletes so unrelated rows remain usable; create can use a separate flag because no persistent id exists yet.
  • For optimistic writes on replica data, use slice.optimistic / entity.optimistic; a rejected call rolls back on its own, deletes included.
  • For optimistic create/update on legacy stores, use the store's reducer/dispatch convention and restore or revalidate affected state if the service fails. Do not leave a temporary row or a successful-looking edit after rejection. Delete only after server success there: a hand-written reducer has no rollback base.
  • Let failures reach the caller's error UI; a finally block clears pending state but does not by itself recover an optimistic write.

Render loading, errors, and settled data

Consume the read hook's SWR response. Success-only flags such as isInit, missing map entries, and data ?? [] cannot distinguish an initial request failure from loading or empty results.

Use AsyncBoundary for standard loading/error/empty/data surfaces; use AsyncError for custom layouts and inline failures. Pass the original SWR data to the boundary: undefined means no successful result, whereas [] is a settled empty result.

Replica useSync returns no data. Feed AsyncBoundary its error and a settled signal from the store entry (data={isListInit || undefined}, where the selector is entry !== undefined); see src/features/Pages/PageLayout/Body/index.tsx.

const BenchmarkList = () => {
  const useFetchBenchmarks = useEvalStore((s) => s.useFetchBenchmarks);
  const benchmarks = useEvalStore((s) => s.benchmarkList);
  const { data, error, isLoading, mutate } = useFetchBenchmarks();

  return (
    <AsyncBoundary
      data={data}
      empty={<EmptyState />}
      error={error}
      isEmpty={data?.length === 0}
      isLoading={isLoading}
      onRetry={() => {
        void mutate();
      }}
    >
      <BenchmarkCards items={benchmarks} />
    </AsyncBoundary>
  );
};

The example's EmptyState and BenchmarkCards stand for the surface's existing renderers. Follow these distinctions when adapting it:

  • Show first-load errors before empty / NotFound / zero-value defaults. Do not put an error branch after if (!map[id]) return <Skeleton />; failure may never fill the map.
  • Preserve settled content during background revalidation failures, including a successfully loaded empty list. A retry in flight should show pending feedback.
  • In a fetched + static list, use the fetched slice's request state before combining rows. Static rows must not hide a failed request.
  • For infinite scroll, keep a per-bucket loadMoreError and show an inline Retry row. Suspend observer-triggered retries while that error is unresolved.
  • A closed modal or absent id disables a request; it is not evidence of a missing record.

Migrating an existing fetch

Move the API call into its service and the request into a store SWR hook. Reuse the existing state shape and action organization unless they need to change for the task. Replace the component's effect/local fetch state with that hook and selectors, then connect error, retry, and pending feedback. Refresh the same cache key after writes.

Check initial failure, retry, settled-empty results, background failure, and switching ids against the surface being changed. For stale data, compare read/refresh keys and the store bucket they update; for stuck loading, inspect rejected requests as well as success callbacks. Do not add a second fetch or another loading flag to mask the cause.

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

acceptance

FreeSummary

Use this skill to verify a delivery by exercising the real product through a suitable surface such as a CLI, web app, desktop app, or iOS Simulator. It helps create or find a verification plan, capture evidence, and publish an acceptance report.

  • Verifying a completed product change
  • Collecting screenshots or other evidence
  • Creating a verification plan and report
lobehub/lobehub83.5KUpdated Oct 26, 2026

Use this skill to add an AI model provider to LobeHub across runtime integration, model cards, settings, branding, documentation, and acceptance checks. It also covers provider documentation updates and distinguishes new providers from models added to an existing provider.

  • Integrating a new AI model provider into LobeHub
  • Updating setup or usage documentation for a provider
  • Checking whether a model needs a new provider integration
lobehub/lobehub83.5KUpdated Oct 26, 2026

add-setting-env

FreeSummary

Use this skill to add a server-side environment variable that sets a default for a user setting. It walks through validation, server configuration and store merging, plus updating the example environment file and self-hosting documentation.

  • Adding an environment variable for a setting default
  • Wiring a new setting domain into server config
  • Documenting a new variable for self-hosting
lobehub/lobehub83.5KUpdated Oct 26, 2026

Provides guidance for registering hooks around an agent’s execution steps, tool calls, human approvals, context compression, and sub-agent calls. Hooks can observe events, supply local mock tool results, or use webhooks to allow or deny tool results under configured policies.

  • Observing agent steps and tool errors
  • Mocking tool results during local execution
  • Requiring webhook decisions before or after a tool call
lobehub/lobehub83.5KUpdated Oct 26, 2026

agent-signal

FreeSummary

Build event-driven background workflows for agents by defining sources, interpreting them as signals, and running actions through policies. It also guides asynchronous handoffs, deduplication, scoped execution, completion receipts, and observability.

  • Adding a new event source to an agent workflow
  • Routing interpreted signals to concrete background actions
  • Enqueuing work for asynchronous processing
lobehub/lobehub83.5KUpdated Oct 26, 2026

Use this skill to run end-to-end acceptance checks for bot channels in real desktop chat apps. It drives apps such as Discord, Slack, Telegram, WeChat, Lark, and QQ to send a message, wait for a response, and capture the result.

  • Checking a bot response in a real Discord channel
  • Testing chat behavior through Slack or Telegram
  • Capturing a bot interaction in a native macOS app
lobehub/lobehub83.5KUpdated Oct 26, 2026

See all skills by lobehub

Report a problem with this skill