# Finqu Storefront SDK A TypeScript SDK for building custom Finqu storefronts with GraphQL, React hooks, and [Puck](https://puckeditor.com/) visual editor support. ## Quick Start Create a new storefront project: ```bash pnpm create @finqu/storefront my-storefront cd my-storefront pnpm install pnpm dev ``` ## Installation ```bash pnpm add @finqu/storefront-sdk ``` ### Peer Dependencies The SDK has optional peer dependencies based on which features you use: ```bash # 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-types ``` ## SDK 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. ```ts 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. ```ts 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 ; } ``` #### Cache Presets ```ts 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`: ```tsx // app/providers.tsx "use client"; import { FinquProvider } from "@finqu/storefront-sdk/react"; export function Providers({ children }: { children: React.ReactNode }) { return ( {children} ); } ``` ### Product Hooks ```tsx "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 ; if (error) return ; if (!data?.product) return ; return ; } 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 ( <> ); } ``` ### Cart Hooks ```tsx "use client"; import { useCart, useCreateCart, useAddToCart, useUpdateCartLines, useRemoveFromCart, useApplyDiscountCode, useUpdateCartBuyerIdentity, useUpdateCartNote, } from "@finqu/storefront-sdk/react"; function AddToCartButton({ id, variantId }: { id: string; variantId: string }) { const [addToCart, { loading }] = useAddToCart(); const handleAdd = () => { addToCart({ variables: { id, lines: [{ merchandiseId: variantId, quantity: 1 }], }, }); }; return ( ); } 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 ( ); } ``` ### ProductGroup (Category) Hooks Finqu uses "ProductGroup" for categories/collections: ```tsx "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 ; const loadMore = () => { const pageInfo = data.productGroup.products.pageInfo; if (pageInfo?.hasNextPage) { fetchMore({ variables: { after: pageInfo.endCursor }, }); } }; return ( <>

{data.productGroup.title}

); } ``` ### Navigation Hook ```tsx "use client"; import { useNavigationMenu } from "@finqu/storefront-sdk/react"; function MainNav() { const { data } = useNavigationMenu({ handle: "main-menu" }); return ( ); } ``` ### Generic Hooks For custom queries and mutations: ```tsx 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: ```ts 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 metadata - `ProductFragment` - Full product with variants, options, manufacturer - `ProductCardFragment` - Minimal product for listings - `ProductVariantFragment` - Variant with pricing, stock, delivery - `ProductVariantOptionFragment` - Variant option selection - `ProductOptionFragment` - Product option with values - `ProductOptionValueFragment` - Product option value - `ProductDiscountFragment` - Product discount information - `DeliveryTimeFragment` - Delivery time information - `ManufacturerFragment` - Product manufacturer - `CombinedListingFragment` - Combined listing data **Cart:** - `CartFragment` - Full cart with lines, discounts, addresses - `CartSummaryFragment` - Minimal cart for header display - `CartLineItemFragment` - Cart line with product details - `CartDiscountFragment` - Cart discount information - `CartTaxLineFragment` - Tax line information - `CartPaymentMethodFragment` - Payment method details - `CartShippingMethodFragment` - Shipping method details - `AddressFragment` - Shipping/billing address **Customer:** - `CustomerFragment` - Full customer with address and loyalty info - `CustomerBasicFragment` - Basic customer information - `CustomerWithAddressFragment` - Customer with default address - `CustomerAccessTokenFragment` - Access token for authentication - `CustomerUserErrorFragment` - Customer operation errors - `UserErrorFragment` - Generic user errors - `CurrencyFragment` - Currency information - `LocaleFragment` - Locale information - `LoyaltyDiscountProgressFragment` - Loyalty program progress **Order:** - `OrderFragment` - Full order with lines, discounts, addresses, payment/shipping methods - `OrderSummaryFragment` - Minimal order for list views (id, orderNumber, status, total) **ProductGroup (Categories):** - `ProductGroupFragment` - Full category with filters and breadcrumbs - `ProductGroupCardFragment` - Category for navigation - `ProductGroupWithBreadcrumbsFragment` - Category with breadcrumb trail - `ProductGroupWithProductsFragment` - Category with products connection - `FilterFragment` - Faceted search filter - `FilterValueFragment` - Filter option value - `PageInfoFragment` - Pagination information --- ## Puck Visual Editor The SDK includes types and utilities for building Puck components. ### Single File Components ```tsx // 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 = { label: "Hero Banner", fields: { title: { type: "text" }, subtitle: { type: "textarea" }, }, render: ({ title, subtitle }) => (

{title}

{subtitle}

), }; // 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 }) => (

{title}

{subtitle}

), }); ``` ### Available Exports ```ts 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 ```ts 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 ```ts 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 ```ts 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 ```ts import { getNavigationMenu, GET_NAVIGATION_MENU } from "@finqu/storefront-sdk/graphql"; ``` ### Server-Only Operations The server module includes customer and order operations that require a secret API key. Customer-scoped queries use the `@storeContext` directive to authenticate via a customer access token. ```ts import { getCustomer, // Fetch authenticated customer getOrders, // Fetch customer orders (paginated) getOrder, // Fetch a single order by ID createCustomerAccessToken, // Login (create access token) deleteCustomerAccessToken, // Logout (delete access token) updateCustomer, // Update customer profile // Raw query strings GET_CUSTOMER, GET_ORDERS, GET_ORDER, CREATE_CUSTOMER_ACCESS_TOKEN, DELETE_CUSTOMER_ACCESS_TOKEN, CUSTOMER_UPDATE, // ... all public operations also available } from "@finqu/storefront-sdk/server"; // Fetch the authenticated customer const { customer } = await getCustomer(client, { customerAccessToken: "eyJhbGciOi...", }); // Fetch customer orders with pagination and sorting const { orders } = await getOrders(client, { customerAccessToken: "eyJhbGciOi...", first: 10, sortKey: "created_at", reverse: true, }); // Fetch a single order const { order } = await getOrder(client, { customerAccessToken: "eyJhbGciOi...", id: 12345, }); // Update customer profile const { customerUpdate } = await updateCustomer(client, { customerAccessToken: "eyJhbGciOi...", input: { firstName: "Jane", lastName: "Doe" }, }); ``` --- ## TypeScript The SDK is fully typed. Import types from `@finqu/storefront-types` for Finqu GraphQL schema types: ```ts import type { Product, Cart, Customer, Order, ProductGroup } from "@finqu/storefront-types"; ``` ---