### Tayori Hooks and Provider Setup Source: https://github.com/sukkaw/tayori/blob/master/packages/tayori/README.md Import and use Tayori's hooks for data fetching, mutations, and infinite loading. Set up the TayoriProvider to initialize the client, potentially injecting authentication tokens. ```tsx // @/lib/tayori.ts import { tayori } from 'tayori'; import type { Options } from 'path/to/hey-api-generated-sdk'; import type { RequestResult } from 'path/to/hey-api-generated-sdk/client'; export const { useData, useInfinite, useMutation, TayoriProvider } = tayori(); import { getData, getAllData, updateData } from 'path/to/hey-api-generated-sdk'; // data fetching export function useGetData() { return useData(getData, { /* request options */ }); } const { data, error, isLoading } = useGetData(); // mutation export function useUpdateData() { return useMutation(updateData); } const { trigger, data, error, isMutating } = useUpdateData(); // in your event handler await trigger({ /* request options */ }); // infinite loading like "Load More" or cursor-based pagination export function useListData() { return useInfinite(getAllData, (pageIndex, previousPageData) => { if (previousPageData && !previousPageData.hasMore) return null; const nextCursor = previousPageData?.meta?.nextCursor; if (!nextCursor && pageIndex > 0) return null; return { /* request options */ query: { cursor: previousPageData?.meta?.nextCursor } }; }); } // `data` is an array of pages const { data: pages, size, setSize, isLoading } = useListData(); // wrap around your app with your own provider. 'use client'; import { createClient } from 'path/to/hey-api-generated-sdk/client'; export function RootProvider({ children }: React.PropsWithChildren) { // you can inject auth to your client here since it is within React // const { getTokenSilently } = useExampleAuth(); return ( createClient(/* Hey API SDK Client options */)}> {children} ); } ``` -------------------------------- ### Initialize Tayori Hooks and Provider Source: https://github.com/sukkaw/tayori/blob/master/README.md Import and destructure hooks and the provider from the tayori library. This setup is required before using any of the data fetching functionalities. Ensure you have the correct types for your options and request results. ```tsx // @/lib/tayori.ts import { tayori } from 'tayori'; import type { Options } from 'path/to/hey-api-generated-sdk'; import type { RequestResult } from 'path/to/hey-api-generated-sdk/client'; export const { useData, useInfinite, useMutation, TayoriProvider } = tayori(); import { getData, getAllData, updateData } from 'path/to/hey-api-generated-sdk'; // data fetching export function useGetData() { return useData(getData, { /* request options */ }); } const { data, error, isLoading } = useGetData(); // mutation export function useUpdateData() { return useMutation(updateData); } const { trigger, data, error, isMutating } = useUpdateData(); // in your event handler await trigger({ /* request options */ }); // infinite loading like "Load More" or cursor-based pagination export function useListData() { return useInfinite(getAllData, (pageIndex, previousPageData) => { if (previousPageData && !previousPageData.hasMore) return null; const nextCursor = previousPageData?.meta?.nextCursor; if (!nextCursor && pageIndex > 0) return null; return { /* request options */ query: { cursor: previousPageData?.meta?.nextCursor } }; }); } // `data` is an array of pages const { data: pages, size, setSize, isLoading } = useListData(); // wrap around your app with your own provider. 'use client'; import { createClient } from 'path/to/hey-api-generated-sdk/client'; export function RootProvider({ children }: React.PropsWithChildren) { // you can inject auth to your client here since it is within React // const { getTokenSilently } = useExampleAuth(); return ( createClient(/* Hey API SDK Client options */)}> {children} ); } ``` -------------------------------- ### Configure TayoriProvider with Client Initialization Source: https://context7.com/sukkaw/tayori/llms.txt Wrap your application with `TayoriProvider` to initialize the Hey API client and configure SWR middleware. The `initClient` callback allows injecting authentication and other client options. Global SWR error handling can be configured via nested `SWRConfig`. ```tsx 'use client'; import { TayoriProvider } from '@/lib/tayori'; import { createClient } from '@/sdk/client'; import { SWRConfig } from 'swr'; import { isZodError } from 'tayori'; import { prettifyError } from 'zod'; import { toast } from 'sonner'; export default function DataFetchingProvider({ children }: React.PropsWithChildren) { const { getAccessTokenSilently } = useAuth(); // any React auth hook return ( createClient({ baseUrl: 'https://api.example.com', throwOnError: true, async auth() { return getAccessTokenSilently(); // inject auth token }, kyOptions: { timeout: 5000, throwHttpErrors: true, retry: { limit: 2, retryOnTimeout: true } } }) } > {/* Optionally nest an additional SWRConfig for global error handling */} {children} ); } ``` -------------------------------- ### useInfinite — Cursor and offset-based pagination Source: https://context7.com/sukkaw/tayori/llms.txt Wraps `useSWRInfinite` for handling paginated data. The second argument is a key loader callback that returns SDK request options or `null`/`false` to stop loading. Suitable for 'Load More' buttons or infinite scroll. ```APIDOC ## `useInfinite` — Cursor and offset-based pagination `useInfinite` wraps `useSWRInfinite`. The second argument is a key loader callback identical to SWR's `getKey`, receiving `(pageIndex, previousPageData)` and returning SDK request options or `null`/`false` to stop loading. Use it for "Load More" buttons or infinite scroll; for simple page-number pagination, `useData` with `query: { page }` is simpler. ```tsx // src/lib/tayori.ts import { getAllPlanets } from '@/sdk'; const PAGE_SIZE = 10; // Offset-based pagination export function usePlanetsInfinite() { return useInfinite( getAllPlanets, (pageIndex, previousPageData) => { const total = previousPageData?.meta?.total; if (total != null && pageIndex * PAGE_SIZE >= total) return null; // end of list return { query: { offset: pageIndex * PAGE_SIZE, limit: PAGE_SIZE } }; } ); } // Cursor-based pagination export function usePlanetsCursor() { return useInfinite( getAllPlanets, (pageIndex, previousPageData) => { if (previousPageData && !previousPageData.meta?.nextCursor) return null; return { query: { cursor: previousPageData?.meta?.nextCursor } }; } ); } // src/app/components/planet-infinite-list.tsx 'use client'; import { usePlanetsInfinite } from '@/lib/tayori'; export function PlanetInfiniteList() { const { data: pages, error, size, setSize, isLoading, isValidating } = usePlanetsInfinite(); const planets = pages?.flatMap((page) => page.data ?? []) ?? []; const lastPage = pages?.[pages.length - 1]; const total = lastPage?.meta?.total; const isReachingEnd = total != null && size * 10 >= total; const isLoadingMore = isValidating && pages?.length === size; if (isLoading) return

Loading...

; if (error) return

Failed to load: {String(error)}

; return ( <>
    {planets.map((p) =>
  • {p.name}
  • )}
); } ``` ``` -------------------------------- ### Implement Cursor-Based Pagination with useInfinite Source: https://context7.com/sukkaw/tayori/llms.txt Use `useInfinite` for cursor-based pagination. The key loader callback uses the `nextCursor` from the previous page's data to fetch the subsequent page, stopping when no `nextCursor` is available. ```typescript // src/lib/tayori.ts import { getAllPlanets } from '@/sdk'; // Cursor-based pagination export function usePlanetsCursor() { return useInfinite( getAllPlanets, (pageIndex, previousPageData) => { if (previousPageData && !previousPageData.meta?.nextCursor) return null; return { query: { cursor: previousPageData?.meta?.nextCursor } }; } ); } ``` -------------------------------- ### Implement Offset-Based Pagination with useInfinite Source: https://context7.com/sukkaw/tayori/llms.txt Use `useInfinite` for offset-based pagination. The key loader callback determines the request options for each page, stopping when the end of the list is reached. ```typescript // src/lib/tayori.ts import { getAllPlanets } from '@/sdk'; const PAGE_SIZE = 10; // Offset-based pagination export function usePlanetsInfinite() { return useInfinite( getAllPlanets, (pageIndex, previousPageData) => { const total = previousPageData?.meta?.total; if (total != null && pageIndex * PAGE_SIZE >= total) return null; // end of list return { query: { offset: pageIndex * PAGE_SIZE, limit: PAGE_SIZE } }; } ); } ``` -------------------------------- ### Use useMutation for triggered writes and on-demand fetches Source: https://context7.com/sukkaw/tayori/llms.txt Use `useMutation` to imperatively call SDK methods from event handlers or `useEffect`. Set `populateCache: true` to write mutation results into the SWR cache for subsequent reuse by `useData`. The `reset()` function clears state and discards in-flight results. ```tsx import { createPlanet, getPlanet } from '@/sdk'; // Standard write mutation export function useCreatePlanet() { return useMutation(createPlanet, { onSuccess(data, key) { console.log('Created:', data.name, 'key:', key); }, onError(err, key) { console.error('Failed:', err, 'key:', key); } }); } // Fetch on demand with cache population export function useFetchPlanetOnDemand() { return useMutation(getPlanet, { populateCache: true }); } ``` ```tsx 'use client'; import { useCreatePlanet } from '@/lib/tayori'; import { isZodError } from 'tayori'; import { prettifyError } from 'zod'; export function PlanetForm() { const { trigger, isMutating, data, error, reset } = useCreatePlanet(); async function handleSubmit(fd: FormData) { try { const result = await trigger({ body: { name: fd.get('name') as string, type: fd.get('type') as string | undefined, description: fd.get('description') as string | undefined } }); console.log('Planet id:', result.id); } catch { // error is also stored in `error` state above } } return (
{error && (

{isZodError(error) ? prettifyError(error) : String(error)}

)} {data &&

Created: {data.name} (id: {data.id})

} {(data || error) && }
); } ``` -------------------------------- ### Use isInternalSWRKey to guard Tayori SWR keys Source: https://context7.com/sukkaw/tayori/llms.txt Employ `isInternalSWRKey` to distinguish SWR keys generated by Tayori hooks (`useData`, `useDataImmutable`, `useInfinite`) from other SWR keys. This is useful for custom SWR middleware that needs to selectively intercept only Tayori-managed requests. ```ts import { isInternalSWRKey } from 'tayori'; import type { Middleware } from 'swr'; // Custom middleware that logs only tayori requests const loggingMiddleware: Middleware = (useSWRNext) => (key, fetcher, config) => { if (isInternalSWRKey(key)) { const [sdkMethod, sdkArg, cacheTags] = key; console.log('[tayori]', sdkMethod.name, sdkArg, cacheTags); } return useSWRNext(key, fetcher, config); }; // Apply alongside TayoriProvider's built-in middleware ``` -------------------------------- ### useData — Standard SWR data fetching Source: https://context7.com/sukkaw/tayori/llms.txt Wraps `useSWR` with a Hey API SDK method and its typed request arguments. It pauses the request if a falsy value is passed or returned. All SWR options are forwarded as the optional third argument. It's recommended to wrap `useData` in domain-specific hooks. ```APIDOC ## `useData` — Standard SWR data fetching `useData` wraps `useSWR` with a Hey API SDK method and its typed request arguments. Passing a falsy value or a function that returns a falsy value pauses the request. All SWR options (e.g. `revalidateOnFocus`, `fallbackData`) are forwarded as the optional third argument. Always wrap `useData` in a domain-specific hook rather than calling it directly in components. ```tsx // src/lib/tayori.ts — define typed wrapper hooks import { getData, searchData } from '@/sdk'; // Basic fetch — runs immediately export function useGetItem(id: string) { return useData(getData, { path: { id } }); } // Conditional fetch — pauses when `search` is empty export function useSearchItems(search: string) { return useData(searchData, search ? { query: { search } } : null); } // Function form — pauses if it throws or returns falsy export function useGetDependentData(parent: { childId: string } | null) { return useData(getData, () => ({ path: { id: parent!.childId } // throws if parent is null → request paused })); } // src/app/components/item-list.tsx — consume the wrapper hook 'use client'; import { useGetItem } from '@/lib/tayori'; export function ItemDetail({ id }: { id: string }) { const { data, isLoading, error } = useGetItem(id); if (isLoading) return

Loading...

; if (error) return

Error: {String(error)}

; return

{data?.name}

; } ``` ``` -------------------------------- ### Initialize Tayori Hooks Factory Source: https://context7.com/sukkaw/tayori/llms.txt Call the `tayori()` factory function once in a shared module to create custom hooks. Re-export these hooks for use throughout your project. This factory carries SDK Options and RequestResult type parameters. ```typescript import { tayori } from 'tayori'; import type { Options } from '@/sdk'; // Hey API generated Options type import type { RequestResult } from '@/sdk/client'; // Hey API generated RequestResult type export const { useData, useDataImmutable, useInfinite, useMutation, TayoriProvider } = tayori(); ``` -------------------------------- ### useMutation - Triggered writes and on-demand fetches Source: https://context7.com/sukkaw/tayori/llms.txt `useMutation` provides a `trigger` function to call a SDK method imperatively. It can be used in event handlers, form actions, or `useEffect`. The `reset()` function clears `data` and `error` state and discards any in-flight mutation result. ```APIDOC ## `useMutation` - Triggered writes and on-demand fetches `useMutation` provides a `trigger` function to call a SDK method imperatively — in event handlers, form actions, or `useEffect`. Unlike SWR's `useSWRMutation`, it does not share a cache key with `useData` by default. Set `populateCache: true` to write the mutation result into the SWR cache so a subsequent `useData` with the same arguments can reuse it without a refetch. The `reset()` function clears `data` and `error` state and discards any in-flight mutation result. ### Example: Standard write mutation ```tsx // src/lib/tayori.ts import { createPlanet } from '@/sdk'; export function useCreatePlanet() { return useMutation(createPlanet, { onSuccess(data, key) { console.log('Created:', data.name, 'key:', key); }, onError(err, key) { console.error('Failed:', err, 'key:', key); } }); } ``` ### Example: Fetch on demand with cache population ```tsx // src/lib/tayori.ts import { getPlanet } from '@/sdk'; export function useFetchPlanetOnDemand() { return useMutation(getPlanet, { populateCache: true }); } ``` ### Example: Usage in a component ```tsx // src/app/components/planet-form.tsx 'use client'; import { useCreatePlanet } from '@/lib/tayori'; import { isZodError } from 'tayori'; import { prettifyError } from 'zod'; export function PlanetForm() { const { trigger, isMutating, data, error, reset } = useCreatePlanet(); async function handleSubmit(fd: FormData) { try { const result = await trigger({ body: { name: fd.get('name') as string, type: fd.get('type') as string | undefined, description: fd.get('description') as string | undefined } }); console.log('Planet id:', result.id); } catch { // error is also stored in `error` state above } } return (
{error && (

{isZodError(error) ? prettifyError(error) : String(error)}

)} {data &&

Created: {data.name} (id: {data.id})

} {(data || error) && }
); } ``` ``` -------------------------------- ### Consume usePlanetsInfinite Hook for Infinite List Source: https://context7.com/sukkaw/tayori/llms.txt Consume the `usePlanetsInfinite` hook to display a list of planets with a 'Load More' button. Manages loading states and determines when the end of the list is reached. ```typescript 'use client'; import { usePlanetsInfinite } from '@/lib/tayori'; export function PlanetInfiniteList() { const { data: pages, error, size, setSize, isLoading, isValidating } = usePlanetsInfinite(); const planets = pages?.flatMap((page) => page.data ?? []) ?? []; const lastPage = pages?.[pages.length - 1]; const total = lastPage?.meta?.total; const isReachingEnd = total != null && size * 10 >= total; const isLoadingMore = isValidating && pages?.length === size; if (isLoading) return

Loading...

; if (error) return

Failed to load: {String(error)}

; return ( <>
    {planets.map((p) =>
  • {p.name}
  • )}
); } ``` -------------------------------- ### isInternalSWRKey — Tayori SWR key guard Source: https://context7.com/sukkaw/tayori/llms.txt `isInternalSWRKey` distinguishes SWR keys created by Tayori hooks (`useData`, `useDataImmutable`, `useInfinite`) from other SWR keys. It's useful for custom SWR middleware to selectively intercept Tayori-managed requests. ```APIDOC ## `isInternalSWRKey` — Tayori SWR key guard `isInternalSWRKey` distinguishes SWR keys created by `useData`/`useDataImmutable`/`useInfinite` from any other `useSWR` key in the same tree. Use it when writing custom SWR middleware to selectively intercept only tayori-managed requests. ### Example: Custom SWR middleware ```ts import { isInternalSWRKey } from 'tayori'; import type { Middleware } from 'swr'; // Custom middleware that logs only tayori requests const loggingMiddleware: Middleware = (useSWRNext) => (key, fetcher, config) => { if (isInternalSWRKey(key)) { const [sdkMethod, sdkArg, cacheTags] = key; console.log('[tayori]', sdkMethod.name, sdkArg, cacheTags); } return useSWRNext(key, fetcher, config); }; // Apply alongside TayoriProvider's built-in middleware ``` ``` -------------------------------- ### Define Typed Wrapper Hooks with useData Source: https://context7.com/sukkaw/tayori/llms.txt Use `useData` to wrap SDK methods for typed request arguments. Passing a falsy value or a function returning falsy pauses the request. All SWR options are forwarded. Always wrap `useData` in a domain-specific hook. ```typescript import { getData, searchData } from '@/sdk'; // Basic fetch — runs immediately export function useGetItem(id: string) { return useData(getData, { path: { id } }); } // Conditional fetch — pauses when `search` is empty export function useSearchItems(search: string) { return useData(searchData, search ? { query: { search } } : null); } // Function form — pauses if it throws or returns falsy export function useGetDependentData(parent: { childId: string } | null) { return useData(getData, () => ({ path: { id: parent!.childId } // throws if parent is null → request paused })); } ``` -------------------------------- ### useDataImmutable — Fetch-once immutable data Source: https://context7.com/sukkaw/tayori/llms.txt Wraps `useSWRImmutable` for fetching data only once without revalidation. Ideal for reference data that rarely changes. The API is identical to `useData`. ```APIDOC ## `useDataImmutable` — Fetch-once immutable data `useDataImmutable` wraps `useSWRImmutable`, meaning the data is fetched once and never revalidated on focus, reconnect, or interval. The API is identical to `useData`. Use it for reference data that changes infrequently (e.g. enumerations, configuration). ```tsx import { getCountries } from '@/sdk'; // Fetched once; never revalidated export function useCountries() { return useDataImmutable(getCountries, {}); } // src/app/components/country-selector.tsx 'use client'; import { useCountries } from '@/lib/tayori'; export function CountrySelector() { const { data } = useCountries(); return ( ); } ``` ``` -------------------------------- ### Fetch Once Immutable Data with useDataImmutable Source: https://context7.com/sukkaw/tayori/llms.txt Use `useDataImmutable` to fetch data only once, suitable for reference data that changes infrequently. It wraps `useSWRImmutable` and never revalidates. ```typescript import { getCountries } from '@/sdk'; // Fetched once; never revalidated export function useCountries() { return useDataImmutable(getCountries, {}); } ``` -------------------------------- ### Consume useCountries Hook in Component Source: https://context7.com/sukkaw/tayori/llms.txt Consume the `useCountries` hook to populate a select dropdown. Assumes `data.items` is an array of country objects with `code` and `name` properties. ```typescript 'use client'; import { useCountries } from '@/lib/tayori'; export function CountrySelector() { const { data } = useCountries(); return ( ); } ``` -------------------------------- ### Consume useData Wrapper Hook in Component Source: https://context7.com/sukkaw/tayori/llms.txt Consume the custom hook `useGetItem` within a React component. Handles loading and error states before rendering data. ```typescript 'use client'; import { useGetItem } from '@/lib/tayori'; export function ItemDetail({ id }: { id: string }) { const { data, isLoading, error } = useGetItem(id); if (isLoading) return

Loading...

; if (error) return

Error: {String(error)}

; return

{data?.name}

; } ``` -------------------------------- ### Invalidate Cache Entries with '#planets' Tag Source: https://context7.com/sukkaw/tayori/llms.txt Use `unstable_mutateWithTags` to invalidate all SWR cache entries tagged with '#planets'. This is useful after a mutation that modifies planet data to ensure subsequent reads fetch the latest information. ```tsx import { unstable_mutateWithTags } from 'tayori'; import { useData, useMutation } from '@/lib/tayori'; import { getPlanets, createPlanet } from '@/sdk'; // Tag reads with '#planets' function usePlanets() { return useData(getPlanets, { query: {}, cacheTags: ['#planets'] }); } // After mutation, invalidate all '#planets' caches function useCreatePlanetWithInvalidation() { return useMutation(createPlanet, { async onSuccess() { await unstable_mutateWithTags(['#planets']); // re-fetches all tagged useData hooks } }); } function PlanetsPage() { const { data } = usePlanets(); const { trigger } = useCreatePlanetWithInvalidation(); return ( <>
    {data?.items.map((p) =>
  • {p.name}
  • )}
); } ``` -------------------------------- ### Use isZodError to guard against Zod validation errors Source: https://context7.com/sukkaw/tayori/llms.txt Use `isZodError` to check if an unknown value is a `ZodError`, typically in error handling scenarios like `SWRConfig.onError` or mutation callbacks. This helps differentiate validation errors from other types of errors. ```tsx import { isZodError } from 'tayori'; import { prettifyError } from 'zod'; function renderError(err: unknown): string { if (isZodError(err)) { // Response failed Zod schema validation (Hey API schema mismatch) return `Validation error: ${prettifyError(err)}`; } // Network error, HTTP 4xx/5xx, etc. return `Request failed: ${String(err)}`; } // Usage in SWRConfig global error handler toast.error(renderError(err)) }}> ``` -------------------------------- ### isZodError — Zod validation error guard Source: https://context7.com/sukkaw/tayori/llms.txt `isZodError` is a guard that checks whether an unknown value is a `ZodError`. Use it to differentiate validation errors from network/HTTP errors in callbacks or error rendering. ```APIDOC ## `isZodError` — Zod validation error guard `isZodError` is a duck-typed guard that checks whether an unknown value is a `ZodError` (i.e. has an `issues` array). Use it in `SWRConfig.onError`, component error rendering, or mutation callbacks to differentiate validation errors from network/HTTP errors. ### Example: Rendering errors ```tsx import { isZodError } from 'tayori'; import { prettifyError } from 'zod'; function renderError(err: unknown): string { if (isZodError(err)) { // Response failed Zod schema validation (Hey API schema mismatch) return `Validation error: ${prettifyError(err)}`; } // Network error, HTTP 4xx/5xx, etc. return `Request failed: ${String(err)}`; } // Usage in SWRConfig global error handler toast.error(renderError(err)) }}> ``` ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.