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