Finqu Storefront SDK
A TypeScript SDK for building custom Finqu storefronts with GraphQL, React hooks, and Puck visual editor support.
Quick Start
Create a new storefront project:
pnpm create @finqu/storefront my-storefront
cd my-storefront
pnpm install
pnpm devInstallation
pnpm add @finqu/storefront-sdkPeer Dependencies
The SDK has optional peer dependencies based on which features you use:
# For GraphQL client (framework-agnostic)
pnpm add graphql graphql-request
# For React hooks (client components)
pnpm add graphql @apollo/client react
# For Puck visual editor
pnpm add @puckeditor/core
# For typed GraphQL responses
pnpm add @finqu/storefront-typesSDK Modules
The SDK provides four entry points for different use cases:
| Import Path | Use Case | Environment |
|---|---|---|
@finqu/storefront-sdk | Puck component types and utilities | Universal |
@finqu/storefront-sdk/graphql | Framework-agnostic GraphQL client | Universal |
@finqu/storefront-sdk/server | Server-side client with caching | Server only |
@finqu/storefront-sdk/react | React hooks with Apollo | Client components |
GraphQL Client
Public Client (Framework-Agnostic)
Uses graphql-request under the hood. Works anywhere JavaScript runs.
import { createFinquClient, getProduct, getProductByHandle, getCatalogProducts } from "@finqu/storefront-sdk/graphql";
const client = createFinquClient({
publicKey: process.env.FINQU_PUBLIC_KEY!,
endpoint: process.env.FINQU_ENDPOINT!,
});
// Using typed helper functions
const { product } = await getProduct(client, { id: "123" });
const { product: productByHandle } = await getProductByHandle(client, { handle: "my-product" });
const { catalog } = await getCatalogProducts(client, { first: 20 });Server Client (Next.js Optimized)
For Server Components, API routes, and server-side rendering with Next.js fetch caching.
import { createFinquServerClient, getProduct, getProductByHandle, cachePresets } from "@finqu/storefront-sdk/server";
const client = createFinquServerClient({
secretKey: process.env.FINQU_SECRET_KEY!, // fq_secret_* key
endpoint: process.env.FINQU_ENDPOINT!,
});
// Server Component with ISR caching
export default async function ProductPage({ params }) {
const { product } = await getProductByHandle(
client,
{ handle: params.handle },
cachePresets.products // { next: { revalidate: 60 } }
);
return <ProductDetails product={product} />;
}Cache Presets
import { cachePresets } from "@finqu/storefront-sdk/server";
cachePresets.static; // Revalidate every hour (3600s)
cachePresets.products; // Revalidate every 60s
cachePresets.dynamic; // No cache (no-store)
// With revalidation tags for on-demand invalidation
cachePresets.withTags(["product", `product:${handle}`]);React Hooks
Apollo-based hooks for client components with automatic caching.
Setup
Wrap your app with FinquProvider:
// app/providers.tsx
"use client";
import { FinquProvider } from "@finqu/storefront-sdk/react";
export function Providers({ children }: { children: React.ReactNode }) {
return (
<FinquProvider
publicKey={process.env.NEXT_PUBLIC_FINQU_PUBLIC_KEY!}
endpoint={process.env.NEXT_PUBLIC_FINQU_ENDPOINT!}
>
{children}
</FinquProvider>
);
}Product Hooks
"use client";
import {
useProduct,
useProductByHandle,
useCatalogProducts,
useCatalogProductsLazy,
} from "@finqu/storefront-sdk/react";
function ProductPage({ id }: { id: string }) {
const { data, loading, error } = useProduct({ id });
if (loading) return <Skeleton />;
if (error) return <Error message={error.message} />;
if (!data?.product) return <NotFound />;
return <ProductDetails product={data.product} />;
}
function ProductGrid() {
const { data, loading, fetchMore } = useCatalogProducts({
first: 12,
sortKey: "created",
reverse: true,
});
const loadMore = () => {
const pageInfo = data?.catalog.products.pageInfo;
if (pageInfo?.hasNextPage) {
fetchMore({
variables: { after: pageInfo.endCursor },
});
}
};
return (
<>
<FilterSidebar filters={data?.catalog.products.filters} />
<Grid products={data?.catalog.products.nodes} />
<Button onClick={loadMore}>Load More</Button>
</>
);
}Cart Hooks
"use client";
import {
useCart,
useCreateCart,
useAddToCart,
useUpdateCartLines,
useRemoveFromCart,
useApplyDiscountCode,
useUpdateCartBuyerIdentity,
useUpdateCartNote,
} from "@finqu/storefront-sdk/react";
function AddToCartButton({ cartId, variantId }: { cartId: string; variantId: string }) {
const [addToCart, { loading }] = useAddToCart();
const handleAdd = () => {
addToCart({
variables: {
cartId,
lines: [{ merchandiseId: variantId, quantity: 1 }],
},
});
};
return (
<Button onClick={handleAdd} disabled={loading}>
Add to Cart
</Button>
);
}
function CreateCartButton({ variantId }: { variantId: string }) {
const [createCart, { loading }] = useCreateCart();
const handleClick = async () => {
const { data } = await createCart({
variables: {
input: {
lines: [{ merchandiseId: variantId, quantity: 1 }],
},
},
});
if (data?.cartCreate.cart) {
localStorage.setItem("cartId", data.cartCreate.cart.id);
}
};
return (
<Button onClick={handleClick} disabled={loading}>
Add to Cart
</Button>
);
}ProductGroup (Category) Hooks
Finqu uses “ProductGroup” for categories/collections:
"use client";
import {
useProductGroup,
useProductGroupByHandle,
useProductGroupWithProducts,
useProductGroups,
useProductGroupsLazy,
} from "@finqu/storefront-sdk/react";
function CategoryPage({ handle }: { handle: string }) {
const [activeFilters, setActiveFilters] = useState([]);
const { data, loading, fetchMore } = useProductGroupWithProducts({
handle,
first: 20,
filters: activeFilters,
});
if (!data?.productGroup) return <NotFound />;
const loadMore = () => {
const pageInfo = data.productGroup.products.pageInfo;
if (pageInfo?.hasNextPage) {
fetchMore({
variables: { after: pageInfo.endCursor },
});
}
};
return (
<>
<h1>{data.productGroup.title}</h1>
<FilterSidebar filters={data.productGroup.products.filters} onFilterChange={setActiveFilters} />
<ProductGrid products={data.productGroup.products.nodes} />
<Button onClick={loadMore}>Load More</Button>
</>
);
}Navigation Hook
"use client";
import { useNavigationMenu } from "@finqu/storefront-sdk/react";
function MainNav() {
const { data } = useNavigationMenu({ handle: "main-menu" });
return (
<nav>
{data?.menu?.links.map((link, index) => (
<NavLink key={index} link={link} />
))}
</nav>
);
}Generic Hooks
For custom queries and mutations:
import { useFinquQuery, useFinquLazyQuery, useFinquMutation } from "@finqu/storefront-sdk/react";
// Custom query
const { data } = useFinquQuery(MY_CUSTOM_QUERY, { variables: { id: "123" } });
// Lazy query (triggered manually)
const [loadData, { data, loading }] = useFinquLazyQuery(MY_CUSTOM_QUERY);
// Mutation
const [mutate, { loading }] = useFinquMutation(MY_CUSTOM_MUTATION);GraphQL Fragments
Pre-built fragments for common Finqu types. Use them to build custom queries:
import { ProductFragment, CartFragment, ProductGroupFragment } from "@finqu/storefront-sdk/graphql";
const MY_CUSTOM_QUERY = `
query GetProductWithRelated($handle: String!) {
product(handle: $handle) {
...Product
}
}
${ProductFragment}
`;Available Fragments
Product:
ImageFragment- Image with dimensions and metadataProductFragment- Full product with variants, options, manufacturerProductCardFragment- Minimal product for listingsProductVariantFragment- Variant with pricing, stock, deliveryProductVariantOptionFragment- Variant option selectionProductOptionFragment- Product option with valuesProductOptionValueFragment- Product option valueProductDiscountFragment- Product discount informationDeliveryTimeFragment- Delivery time informationManufacturerFragment- Product manufacturerCombinedListingFragment- Combined listing data
Cart:
CartFragment- Full cart with lines, discounts, addressesCartSummaryFragment- Minimal cart for header displayCartLineItemFragment- Cart line with product detailsCartDiscountFragment- Cart discount informationCartTaxLineFragment- Tax line informationCartPaymentMethodFragment- Payment method detailsCartShippingMethodFragment- Shipping method detailsAddressFragment- Shipping/billing address
Customer:
CustomerFragment- Full customer with address and loyalty infoCustomerBasicFragment- Basic customer informationCustomerWithAddressFragment- Customer with default addressCustomerAccessTokenFragment- Access token for authenticationCustomerUserErrorFragment- Customer operation errorsUserErrorFragment- Generic user errorsCurrencyFragment- Currency informationLocaleFragment- Locale informationLoyaltyDiscountProgressFragment- Loyalty program progress
ProductGroup (Categories):
ProductGroupFragment- Full category with filters and breadcrumbsProductGroupCardFragment- Category for navigationProductGroupWithBreadcrumbsFragment- Category with breadcrumb trailProductGroupWithProductsFragment- Category with products connectionFilterFragment- Faceted search filterFilterValueFragment- Filter option valuePageInfoFragment- Pagination information
Puck Visual Editor
The SDK includes types and utilities for building Puck components.
Single File Components
// components/Hero.puck.tsx
import { defineComponent, type ComponentConfig } from "@finqu/storefront-sdk";
interface HeroProps {
title: string;
subtitle: string;
}
export const category = "Marketing";
export const config: ComponentConfig<HeroProps> = {
label: "Hero Banner",
fields: {
title: { type: "text" },
subtitle: { type: "textarea" },
},
render: ({ title, subtitle }) => (
<section>
<h1>{title}</h1>
<p>{subtitle}</p>
</section>
),
};
// Or use the defineComponent helper for type inference
export const config = defineComponent({
label: "Hero Banner",
fields: {
title: { type: "text" },
subtitle: { type: "textarea" },
},
render: ({ title, subtitle }) => (
<section>
<h1>{title}</h1>
<p>{subtitle}</p>
</section>
),
});Available Exports
import {
defineComponent, // Helper for type-safe component config
defineFields, // Helper for type-safe field definitions
type PuckComponentConfig, // Component config type (deprecated, use ComponentConfig)
type PuckComponentModule, // Module with category and config
type ComponentCategory, // Category string type
type StorefrontConfig, // Full Puck Config type
type Config, // Re-exported from @puckeditor/core
type Data, // Re-exported from @puckeditor/core
type ComponentConfig, // Re-exported from @puckeditor/core
type Fields, // Re-exported from @puckeditor/core
} from "@finqu/storefront-sdk";GraphQL Operations
The SDK exports typed helper functions and raw query strings for all operations:
Product Operations
import {
getProduct, // Fetch product by ID
getProductByHandle, // Fetch product by handle
getCatalogProducts, // Fetch paginated products with filters
GET_PRODUCT, // Raw query string
GET_PRODUCT_BY_HANDLE,
GET_CATALOG_PRODUCTS,
} from "@finqu/storefront-sdk/graphql";Cart Operations
import {
getCart, // Fetch cart by ID
createCart, // Create new cart
addToCart, // Add items to cart
updateCartLines, // Update line quantities
removeFromCart, // Remove items from cart
applyDiscountCode, // Apply discount code
updateCartBuyerIdentity, // Update buyer info
updateCartNote, // Update cart note
// Raw query strings
GET_CART,
CREATE_CART,
ADD_TO_CART,
UPDATE_CART_LINES,
REMOVE_FROM_CART,
APPLY_DISCOUNT_CODE,
UPDATE_CART_BUYER_IDENTITY,
UPDATE_CART_NOTE,
} from "@finqu/storefront-sdk/graphql";ProductGroup Operations
import {
getProductGroup, // Fetch by ID
getProductGroupByHandle, // Fetch by handle
getProductGroupWithProducts, // With products and filters
getProductGroups, // Fetch multiple
// Raw query strings
GET_PRODUCT_GROUP,
GET_PRODUCT_GROUP_BY_HANDLE,
GET_PRODUCT_GROUP_WITH_PRODUCTS,
GET_PRODUCT_GROUPS,
} from "@finqu/storefront-sdk/graphql";Navigation Operations
import { getNavigationMenu, GET_NAVIGATION_MENU } from "@finqu/storefront-sdk/graphql";Server-Only Operations
The server module includes additional operations requiring authentication:
import {
getCustomer, // Fetch customer by ID
getCustomerByToken, // Fetch by access token
getCustomerOrders, // Fetch customer orders
getOrder, // Fetch a single order
createCustomerAccessToken, // Login (create access token)
deleteCustomerAccessToken, // Logout (delete access token)
deleteCustomerAddress, // Delete customer address
// Raw query strings
GET_CUSTOMER,
GET_CUSTOMER_BY_TOKEN,
GET_CUSTOMER_ORDERS,
GET_ORDER,
CREATE_CUSTOMER_ACCESS_TOKEN,
DELETE_CUSTOMER_ACCESS_TOKEN,
DELETE_CUSTOMER_ADDRESS,
// ... all public operations also available
} from "@finqu/storefront-sdk/server";TypeScript
The SDK is fully typed. Import types from @finqu/storefront-types for Finqu GraphQL schema types:
import type { Product, Cart, Customer, ProductGroup } from "@finqu/storefront-types";