@krakentech/blueprint-api
This is our package which houses the utilities for interacting with the Kraken API.
You can install this package via npm
with an organisation token - please get in touch if you need one.
We use the graphql-request and
TanStack Query (FKA React Query) libraries to make API calls.
React Query offers features like caching, background updates, and optimistic updates, making it easier to handle data
fetching and synchronization, and graphql-request makes the network requests.
Blueprint API utilities for queries and mutations
For Blueprint, we have created custom wrapper hooks to handle queries and mutations to Kraken endpoints. These hooks allow us to maintain a consistent integration Kraken across our project. We only need to specify things like error handling and auth checks in one place, which makes it easier to make updates.
Why Use Custom Wrapper Hooks?
By encapsulating common concerns such as authentication, error handling, and session management, these custom hooks:
- Provide a unified interface for interacting with Kraken endpoints.
- Reduce repetitive code in components, keeping them focused on rendering logic.
- Handle complexities like masquerading sessions and conditional query execution.
useKrakenQuery
The useKrakenQuery
hook is used for fetching data from Kraken. It wraps the useQuery
hook from React Query and adds
custom logic for handling authentication and errors.
Example usage in the API reference.
useInfiniteKrakenQuery
The useInfiniteKrakenQuery
hook is used for fetching paginated data from Kraken. It wraps the useInfiniteQuery
hook
from React Query and adds custom logic for handling authentication and errors.
Example usage in the API reference.
useKrakenMutation
The useKrakenMutation
hook is used for performing mutations from Kraken. It wraps the useMutation
hook from
React Query and adds custom logic for handling errors.
Example usage in the API reference.
React Query in Blueprint
In Blueprint, React Query is configured in the CoreProvider
component, located in the core
package (packages/core/src/components/Providers/Core
).
This ensures a centralized and consistent setup for data fetching across the application.
The CoreProvider
initializes a QueryClient
with default options and provides it to the app using the QueryClientProvider
.
Implementation
import { useState, type ReactNode } from 'react';
import { QueryClient, QueryClientProvider, QueryClientConfig } from '@tanstack/react-query';
const defaultQueryClientConfig: QueryClientConfig = {
defaultOptions: {
queries: {
refetchOnMount: false,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
},
},
};
export type CoreProviderProps = {
children: ReactNode;
queryClientConfig?: QueryClientConfig;
};
export const CoreProvider = ({ children, queryClientConfig }: CoreProviderProps) => {
const [queryClient] = useState(
() => new QueryClient(queryClientConfig ?? defaultQueryClientConfig)
);
return (
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
);
};
We can override the default QueryClient
configuration by passing the queryClientConfig
options to the CoreProvider
.
This flexibility ensures our data-fetching strategy adapts to specific use cases.
Query Keys
We use structured query keys to reflect the data hierarchy and ensure uniqueness. This makes it easier to manage caching, updates, and refetching behavior.
Current Approach
- Query keys are constructed as arrays that describe the data being fetched.
- Custom hooks are used to generate consistent query keys, reducing duplication and potential errors.
Example: Query Keys array
const useUser = (userId: string) => useKrakenQuery(['user', userId], fetchUser);
Example: Reusable Query Key Generator
export const generatePageQueryKey = ({ id, locale }: { id: string; locale: string }) => ['page-content', id, locale];
We use these query key generators in components and hooks to maintain consistency:
import { useQuery } from '@tanstack/react-query';
import { fetchPageItem } from '../api/fetchPageItem';
import { generatePageQueryKey } from '../utils/queryKeys';
export const usePageItem = ({ id }: { id: string }) => {
return useKrakenQuery({
queryKey: generatePageQueryKey({ id, locale }),
queryFn: () => fetchPageItem({ id, locale }),
});
};
Selectors
Selectors play a key role in transforming data before it is used in components. This keeps the component logic clean and ensures that only the required data shape is passed down.
Example: Viewer Accounts
const useViewerAccounts = () => useKrakenQuery({
queryKey: ['viewer', 'accounts'],
queryFn: viewerAccountsQuery,
select: (data) => ({
accounts: data.viewer?.accounts,
}),
});
Prefetching in Server-Side Props
For server-side rendering, we don't use the wrappers - we leverage React Query’s prefetching capabilities.
This allows us to fetch data on the server and hydrate it for the client, improving performance and providing a
seamless user experience.
We use a sharedPageProps
hook, located in the utils
directory of each app(e.g, apps/demo-site/src/utils/props
),
to handle props that are shared by every page. This hook is called in every new page to retrieve server-side props
required for prefetching.
This ensures consistency in fetching common data and simplifies the integration of shared props across the application.
Example: Prefetching in Server-Side Props
import { QueryClient } from '@tanstack/react-query';
export const getServerSideProps: GetServerSideProps = async (context) => {
const queryClient = new QueryClient();
return {
props: await sharedPageProps({ queryClient, context }),
};
};