DEV Community

pjdev2d
pjdev2d

Posted on

tanstack flow


export { dashboardKeys } from "./keys";
export { dashboardMappers } from "./mappers";
export { dashboardMocks } from "./mocks";
export { dashboardService } from "./services";
export { dashboardQueries } from "./queries";
export { dashboardMutations } from "./mutations";
export type * from "./types";
Enter fullscreen mode Exit fullscreen mode
export const dashboardKeys = {
  all: () => ["dashboard"] as const,
  stats: () => [...dashboardKeys.all(), "stats"] as const,
  revenue: () => [...dashboardKeys.all(), "revenue"] as const,
  alerts: () => [...dashboardKeys.all(), "alerts"] as const,
};

Enter fullscreen mode Exit fullscreen mode
import { mutationOptions, type QueryClient } from "@tanstack/react-query";
import { toast } from "@/components/base";
import { dashboardKeys } from "./keys";
import { dashboardService } from "./services";
import type { CreateAlertInput } from "./types";

export const dashboardMutations = {
  createAlert: (queryClient: QueryClient) =>
    mutationOptions({
      mutationFn: (input: CreateAlertInput) => dashboardService.createAlert(input),
      onMutate: () => {
        toast.info({
          id: "dashboard_create_alert_submitting",
          title: "Submitting…",
          duration: 0,
        });
      },
      onSuccess: async () => {
        toast.dismiss("dashboard_create_alert_submitting");
        await queryClient.invalidateQueries({ queryKey: dashboardKeys.alerts() });
        toast.success({ title: "Submitted" });
      },
      onError: (error) => {
        toast.dismiss("dashboard_create_alert_submitting");
        toast.error({
          title: "Submission failed",
          description: error instanceof Error ? error.message : undefined,
        });
      },
    }),
};

Enter fullscreen mode Exit fullscreen mode
import { queryOptions } from "@tanstack/react-query";

import { dashboardKeys } from "./keys";
import { dashboardMappers } from "./mappers";
import { dashboardMocks } from "./mocks";
import { dashboardService } from "./services";

export const dashboardQueries = {
  stats: () =>
    queryOptions({
      queryKey: dashboardKeys.stats(),
      queryFn: async () => {
        try {
          const raw = await dashboardService.getStats();
          return dashboardMappers.statCards(raw as any);
        } catch {
          return dashboardMappers.statCards(dashboardMocks.stats());
        }
      },
    }),

  revenue: () =>
    queryOptions({
      queryKey: dashboardKeys.revenue(),
      queryFn: async () => {
        try {
          return await dashboardService.getRevenue();
        } catch {
          return dashboardMocks.revenue();
        }
      },
    }),

  alerts: () =>
    queryOptions({
      queryKey: dashboardKeys.alerts(),
      queryFn: async () => {
        try {
          const raw = await dashboardService.getAlerts();
          return dashboardMappers.alerts(raw as any);
        } catch {
          return dashboardMappers.alerts(dashboardMocks.alerts());
        }
      },
    }),
};

Enter fullscreen mode Exit fullscreen mode
import { apiClient } from "@/services/http/client";

export const dashboardService = {
  getStats: () => apiClient.get("/dashboard/stats"),
  getRevenue: () => apiClient.get("/dashboard/revenue"),
  getAlerts: () => apiClient.get("/dashboard/alerts"),
  createAlert: (input: any) => apiClient.post("/dashboard/alerts", input),
};

Enter fullscreen mode Exit fullscreen mode

README

# Dashboard Service (TanStack Query)

This folder contains everything needed to fetch, transform, cache, and mutate “Dashboard” data using TanStack Query v5 in a **feature-based** layout (similar to RTK Query).

## How the files connect

**High-level data flow**

1. **Component** calls a predefined query/mutation option
2. **queries.ts / mutations.ts** run a `queryFn` / `mutationFn`
3. **services.ts** performs the HTTP request (API calls only)
4. **mappers.ts** transforms raw API data into UI-ready objects (icons, colors, hrefs, etc.)
5. **mocks.ts** provides fallback data when the API fails (dev-friendly)
6. **keys.ts** defines consistent query keys for caching + invalidation

## File responsibilities

### `keys.ts`
- Centralized query keys for the dashboard namespace.
- Used by queries and mutations to:
  - cache correctly (`queryKey`)
  - invalidate correctly (`invalidateQueries`)

Example:
- `dashboardKeys.alerts()``["dashboard", "alerts"]`

### `services.ts`
- **API calls only** (no UI logic, no mapping).
- Uses the shared HTTP client (`src/services/http/client.ts`) to call endpoints like:
  - `GET /dashboard/stats`
  - `GET /dashboard/revenue`
  - `GET /dashboard/alerts`
  - `POST /dashboard/alerts`

### `mappers.ts`
- **UI transformation only**.
- Converts raw API responses into what components render:
  - icon selection (Lucide)
  - `href` mapping (router `PATHS`)
  - color class names
  - any formatting/shape changes

### `mocks.ts`
- **Fallback/mock responses only**.
- Used inside `try/catch` blocks in `queries.ts` / `mutations.ts`.
- Keeps components stable even if the backend isn’t running.

### `queries.ts`
- Exports **centralized** TanStack Query `queryOptions()` builders.
- Each query:
  - uses `dashboardKeys.*()` for `queryKey`
  - calls `dashboardService.*()` for API data
  - applies `dashboardMappers.*()` for UI-ready data
  - falls back to `dashboardMocks.*()` when the API fails

### `mutations.ts`
- Exports **centralized** TanStack Query `mutationOptions()` builders.
- Mutations handle **automatic invalidation** so components don’t need data-logic.
  - Example: after `createAlert` succeeds → invalidate `dashboardKeys.alerts()`

### `types.ts`
- Shared TypeScript types for:
  - raw API payloads (`Raw*`)
  - UI-ready types (`StatCardData`, `AlertData`)
  - mutation inputs (e.g. `CreateAlertInput`)

### `index.ts`
- Single “barrel” export for the whole feature.
- Makes imports clean and consistent.

## Typical usage in a page/component

Enter fullscreen mode Exit fullscreen mode


ts
import { useSuspenseQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { dashboardQueries, dashboardMutations } from "@/services/dashboard";

const alerts = useSuspenseQuery(dashboardQueries.alerts());

const queryClient = useQueryClient();
const createAlert = useMutation(dashboardMutations.createAlert(queryClient));


## Adding a new endpoint (checklist)

1. Add raw types (if needed) in `types.ts`
2. Add API call in `services.ts`
3. Add mock response in `mocks.ts` (optional but recommended during dev)
4. Add mapper in `mappers.ts` if UI transformation is needed
5. Add key in `keys.ts` (especially if it needs invalidation)
6. Add `queryOptions()` in `queries.ts` **or** `mutationOptions()` in `mutations.ts`
7. Export from `index.ts`


Enter fullscreen mode Exit fullscreen mode

Top comments (0)