mirror of
https://github.com/vercel/commerce.git
synced 2025-03-31 09:15:53 +00:00
* Custom Checkout Progress * Updates to Checkout * Custom Checkout Progress * Adding tabs * Adding Collapse * Adding Collapse * Improving Sidebar Scroll * Modif footer * Changes * More design updates * sidebar cart * More design updates * More design updates * More design updates * More design updates * Types * Types * Design Updates * More changes * More changes * More changes * Changes * Changes * Changes * New tailwind required changes * Sidebar Styling issues with Mobile * Latest changes - Normalizing cart * Styling Fixes * New changes * Changes * latest * Refactor and Renaming some UI Props * Adding Quantity Component * Adding Rating Component * Rating Component * More updates * User Select disabled, plus hidding horizontal scroll bars * Changes * Adding ProductOptions Component and more helpers * Styling updates * Styling updates * Fix for slim tags * Missmatch with RightArrow * Footer updates and some styles * Latest Updates * Latest Updates * Latest Updates * Removing Portal, since it's not needed. We might add it later I'd rather not to. * Removing Portal, since it's not needed. We might add it later I'd rather not to. * Sam backdrop filter * General UI Improvements * General UI Improvements * Search now with Geist Colors * Now with Geist Colors * Changes * Scroll for Mobile on IOs devises * LoadingDots Working (: * Changes * More Changes * Perf changes * More perf changes * Fade to the Nametags in the ProductCard * changes * Search issue ui * Search issue ui * Make sure to only refresh navbar and modals when required * Index revalidate * Fixed image issue * hide album scroll on windows * Fix scrollbar * Changing * Adding 404 with Layout * Removing Toast * Adding Assets * Adding Assets * Progress with LocalProvider * New productTag * Only images for the drop * changes * Empty SWRhooks * Adding Local Provider * Working local * Working view of a LocalProvider * More updates * Changes * Removed react-ticker * default to local if no env available * default to local if no env available * add missing `@` to css import * rewrite search rewrites to multiple pages * allow requests in getStaticProps to execute in parallel * make type import explicit * add a tsconfig.js file * use local provider in tsconfig.js * avoid a circular dependency * Saleor was not in the providers list * avoid circular dependency in bigcommerce * Adding more to the Local Provider (#366) * Adding more data * Adding more data * optimize assets (#370) * Optimize assets (#372) * optimize assets * remove assets * remove assets * cart enabled * Adding saleor * Changes with Webpack * Changes Co-authored-by: Luis Alvarez <luis@vercel.com> Co-authored-by: Tobias Koppers <tobias.koppers@googlemail.com> Co-authored-by: Shu Ding <g@shud.in>
246 lines
7.0 KiB
Markdown
246 lines
7.0 KiB
Markdown
# Adding a new Commerce Provider
|
|
|
|
A commerce provider is a headless e-commerce platform that integrates with the [Commerce Framework](./README.md). Right now we have the following providers:
|
|
|
|
- BigCommerce ([framework/bigcommerce](../bigcommerce))
|
|
- Saleor ([framework/saleor](../saleor))
|
|
- Shopify ([framework/shopify](../shopify))
|
|
|
|
Adding a commerce provider means adding a new folder in `framework` with a folder structure like the next one:
|
|
|
|
- `api`
|
|
- index.ts
|
|
- `product`
|
|
- usePrice
|
|
- useSearch
|
|
- getProduct
|
|
- getAllProducts
|
|
- `wishlist`
|
|
- useWishlist
|
|
- useAddItem
|
|
- useRemoveItem
|
|
- `auth`
|
|
- useLogin
|
|
- useLogout
|
|
- useSignup
|
|
- `customer`
|
|
- useCustomer
|
|
- getCustomerId
|
|
- getCustomerWistlist
|
|
- `cart`
|
|
- useCart
|
|
- useAddItem
|
|
- useRemoveItem
|
|
- useUpdateItem
|
|
- `env.template`
|
|
- `index.ts`
|
|
- `provider.ts`
|
|
- `commerce.config.json`
|
|
- `next.config.js`
|
|
- `README.md`
|
|
|
|
`provider.ts` exports a provider object with handlers for the [Commerce Hooks](./README.md#commerce-hooks) and `api/index.ts` exports a Node.js provider for the [Commerce API](./README.md#commerce-api)
|
|
|
|
> **Important:** We use TypeScript for every provider and expect its usage for every new one.
|
|
|
|
The app imports from the provider directly instead of the core commerce folder (`framework/commerce`), but all providers are interchangeable and to achieve it every provider always has to implement the core types and helpers.
|
|
|
|
The provider folder should only depend on `framework/commerce` and dependencies in the main `package.json`. In the future we'll move the `framework` folder to a package that can be shared easily for multiple apps.
|
|
|
|
## Adding the provider hooks
|
|
|
|
Using BigCommerce as an example. The first thing to do is export a `CommerceProvider` component that includes a `provider` object with all the handlers that can be used for hooks:
|
|
|
|
```tsx
|
|
import type { ReactNode } from 'react'
|
|
import {
|
|
CommerceConfig,
|
|
CommerceProvider as CoreCommerceProvider,
|
|
useCommerce as useCoreCommerce,
|
|
} from '@commerce'
|
|
import { bigcommerceProvider } from './provider'
|
|
import type { BigcommerceProvider } from './provider'
|
|
|
|
export { bigcommerceProvider }
|
|
export type { BigcommerceProvider }
|
|
|
|
export const bigcommerceConfig: CommerceConfig = {
|
|
locale: 'en-us',
|
|
cartCookie: 'bc_cartId',
|
|
}
|
|
|
|
export type BigcommerceConfig = Partial<CommerceConfig>
|
|
|
|
export type BigcommerceProps = {
|
|
children?: ReactNode
|
|
locale: string
|
|
} & BigcommerceConfig
|
|
|
|
export function CommerceProvider({ children, ...config }: BigcommerceProps) {
|
|
return (
|
|
<CoreCommerceProvider
|
|
provider={bigcommerceProvider}
|
|
config={{ ...bigcommerceConfig, ...config }}
|
|
>
|
|
{children}
|
|
</CoreCommerceProvider>
|
|
)
|
|
}
|
|
|
|
export const useCommerce = () => useCoreCommerce<BigcommerceProvider>()
|
|
```
|
|
|
|
The exported types and components extend from the core ones exported by `@commerce`, which refers to `framework/commerce`.
|
|
|
|
The `bigcommerceProvider` object looks like this:
|
|
|
|
```tsx
|
|
import { handler as useCart } from './cart/use-cart'
|
|
import { handler as useAddItem } from './cart/use-add-item'
|
|
import { handler as useUpdateItem } from './cart/use-update-item'
|
|
import { handler as useRemoveItem } from './cart/use-remove-item'
|
|
|
|
import { handler as useWishlist } from './wishlist/use-wishlist'
|
|
import { handler as useWishlistAddItem } from './wishlist/use-add-item'
|
|
import { handler as useWishlistRemoveItem } from './wishlist/use-remove-item'
|
|
|
|
import { handler as useCustomer } from './customer/use-customer'
|
|
import { handler as useSearch } from './product/use-search'
|
|
|
|
import { handler as useLogin } from './auth/use-login'
|
|
import { handler as useLogout } from './auth/use-logout'
|
|
import { handler as useSignup } from './auth/use-signup'
|
|
|
|
import fetcher from './fetcher'
|
|
|
|
export const bigcommerceProvider = {
|
|
locale: 'en-us',
|
|
cartCookie: 'bc_cartId',
|
|
fetcher,
|
|
cart: { useCart, useAddItem, useUpdateItem, useRemoveItem },
|
|
wishlist: {
|
|
useWishlist,
|
|
useAddItem: useWishlistAddItem,
|
|
useRemoveItem: useWishlistRemoveItem,
|
|
},
|
|
customer: { useCustomer },
|
|
products: { useSearch },
|
|
auth: { useLogin, useLogout, useSignup },
|
|
}
|
|
|
|
export type BigcommerceProvider = typeof bigcommerceProvider
|
|
```
|
|
|
|
The provider object, in this case `bigcommerceProvider`, has to match the `Provider` type defined in [framework/commerce](./index.ts).
|
|
|
|
A hook handler, like `useCart`, looks like this:
|
|
|
|
```tsx
|
|
import { useMemo } from 'react'
|
|
import { SWRHook } from '@commerce/utils/types'
|
|
import useCart, { UseCart, FetchCartInput } from '@commerce/cart/use-cart'
|
|
import { normalizeCart } from '../lib/normalize'
|
|
import type { Cart } from '../types'
|
|
|
|
export default useCart as UseCart<typeof handler>
|
|
|
|
export const handler: SWRHook<
|
|
Cart | null,
|
|
{},
|
|
FetchCartInput,
|
|
{ isEmpty?: boolean }
|
|
> = {
|
|
fetchOptions: {
|
|
url: '/api/cart',
|
|
method: 'GET',
|
|
},
|
|
async fetcher({ input: { cartId }, options, fetch }) {
|
|
const data = cartId ? await fetch(options) : null
|
|
return data && normalizeCart(data)
|
|
},
|
|
useHook:
|
|
({ useData }) =>
|
|
(input) => {
|
|
const response = useData({
|
|
swrOptions: { revalidateOnFocus: false, ...input?.swrOptions },
|
|
})
|
|
|
|
return useMemo(
|
|
() =>
|
|
Object.create(response, {
|
|
isEmpty: {
|
|
get() {
|
|
return (response.data?.lineItems.length ?? 0) <= 0
|
|
},
|
|
enumerable: true,
|
|
},
|
|
}),
|
|
[response]
|
|
)
|
|
},
|
|
}
|
|
```
|
|
|
|
In the case of data fetching hooks like `useCart` each handler has to implement the `SWRHook` type that's defined in the core types. For mutations it's the `MutationHook`, e.g for `useAddItem`:
|
|
|
|
```tsx
|
|
import { useCallback } from 'react'
|
|
import type { MutationHook } from '@commerce/utils/types'
|
|
import { CommerceError } from '@commerce/utils/errors'
|
|
import useAddItem, { UseAddItem } from '@commerce/cart/use-add-item'
|
|
import { normalizeCart } from '../lib/normalize'
|
|
import type {
|
|
Cart,
|
|
BigcommerceCart,
|
|
CartItemBody,
|
|
AddCartItemBody,
|
|
} from '../types'
|
|
import useCart from './use-cart'
|
|
|
|
export default useAddItem as UseAddItem<typeof handler>
|
|
|
|
export const handler: MutationHook<Cart, {}, CartItemBody> = {
|
|
fetchOptions: {
|
|
url: '/api/cart',
|
|
method: 'POST',
|
|
},
|
|
async fetcher({ input: item, options, fetch }) {
|
|
if (
|
|
item.quantity &&
|
|
(!Number.isInteger(item.quantity) || item.quantity! < 1)
|
|
) {
|
|
throw new CommerceError({
|
|
message: 'The item quantity has to be a valid integer greater than 0',
|
|
})
|
|
}
|
|
|
|
const data = await fetch<BigcommerceCart, AddCartItemBody>({
|
|
...options,
|
|
body: { item },
|
|
})
|
|
|
|
return normalizeCart(data)
|
|
},
|
|
useHook:
|
|
({ fetch }) =>
|
|
() => {
|
|
const { mutate } = useCart()
|
|
|
|
return useCallback(
|
|
async function addItem(input) {
|
|
const data = await fetch({ input })
|
|
await mutate(data, false)
|
|
return data
|
|
},
|
|
[fetch, mutate]
|
|
)
|
|
},
|
|
}
|
|
```
|
|
|
|
## Adding the Node.js provider API
|
|
|
|
TODO
|
|
|
|
> The commerce API is currently going through a refactor in https://github.com/vercel/commerce/pull/252 - We'll update the docs once the API is released.
|