diff --git a/lib/bigcommerce/api/index.ts b/lib/bigcommerce/api/index.ts index 683be5820..aada33024 100644 --- a/lib/bigcommerce/api/index.ts +++ b/lib/bigcommerce/api/index.ts @@ -1,20 +1,6 @@ -import { - CommerceAPI, - CommerceAPIOptions, - CommerceAPIFetchOptions, -} from 'lib/commerce/api'; -import { GetAllProductsQuery, GetAllProductsQueryVariables } from '../schema'; -import { getAllProductsQuery } from './operations/get-all-products'; - -type RecursivePartial = { - [P in keyof T]?: RecursivePartial; -}; - -export interface GetAllProductsResult { - products: T extends GetAllProductsQuery - ? T['site']['products']['edges'] - : unknown; -} +import { CommerceAPIConfig } from 'lib/commerce/api'; +import { GetAllProductsQueryVariables } from '../schema'; +import fetchAPI from './utils/fetch-api'; export interface Images { small?: ImageOptions; @@ -28,10 +14,6 @@ export interface ImageOptions { height?: number; } -export interface BigcommerceAPIOptions extends CommerceAPIOptions { - images?: Images; -} - export type ProductImageVariables = Pick< GetAllProductsQueryVariables, | 'imgSmallWidth' @@ -44,83 +26,51 @@ export type ProductImageVariables = Pick< | 'imgXLHeight' >; -export type ProductVariables = Images & - Omit; - -export default class BigcommerceAPI implements CommerceAPI { - commerceUrl: string; - apiToken: string; - imageVariables?: ProductImageVariables; - - constructor({ commerceUrl, apiToken, images }: BigcommerceAPIOptions) { - this.commerceUrl = commerceUrl; - this.apiToken = apiToken; - this.imageVariables = { - imgSmallWidth: images?.small?.width, - imgSmallHeight: images?.small?.height, - imgMediumWidth: images?.medium?.height, - imgMediumHeight: images?.medium?.height, - imgLargeWidth: images?.large?.height, - imgLargeHeight: images?.large?.height, - imgXLWidth: images?.xl?.height, - imgXLHeight: images?.xl?.height, - }; - } - - async fetch( - query: string, - { variables, preview }: CommerceAPIFetchOptions = {} - ): Promise { - const res = await fetch(this.commerceUrl + (preview ? '/preview' : ''), { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.apiToken}`, - }, - body: JSON.stringify({ - query, - variables, - }), - }); - - const json = await res.json(); - if (json.errors) { - console.error(json.errors); - throw new Error('Failed to fetch API'); - } - return json.data; - } - - async getAllProducts(opts: { - query: string; - variables?: V; - }): Promise>; - - async getAllProducts(opts?: { - query?: string; - variables?: ProductVariables; - }): Promise>; - - async getAllProducts({ - query = getAllProductsQuery, - variables: vars, - }: { - query?: string; - variables?: ProductVariables; - } = {}): Promise< - GetAllProductsResult> - > { - const variables: GetAllProductsQueryVariables = { - ...this.imageVariables, - ...vars, - }; - const data = await this.fetch>( - query, - { variables } - ); - - return { - products: data?.site?.products?.edges, - }; - } +export interface BigcommerceConfig extends CommerceAPIConfig { + images?: Images; + readonly imageVariables?: ProductImageVariables; +} + +const API_URL = process.env.BIGCOMMERCE_STOREFRONT_API_URL; +const API_TOKEN = process.env.BIGCOMMERCE_STOREFRONT_API_TOKEN; + +if (!API_URL) { + throw new Error( + `The environment variable BIGCOMMERCE_STOREFRONT_API_URL is missing and it's required to access your store` + ); +} + +if (!API_TOKEN) { + throw new Error( + `The environment variable BIGCOMMERCE_STOREFRONT_API_TOKEN is missing and it's required to access your store` + ); +} + +const config: BigcommerceConfig = { + commerceUrl: API_URL, + apiToken: API_TOKEN, + fetch: fetchAPI, + get imageVariables() { + const { images } = this; + return images + ? { + imgSmallWidth: images.small?.width, + imgSmallHeight: images.small?.height, + imgMediumWidth: images.medium?.height, + imgMediumHeight: images.medium?.height, + imgLargeWidth: images.large?.height, + imgLargeHeight: images.large?.height, + imgXLWidth: images.xl?.height, + imgXLHeight: images.xl?.height, + } + : undefined; + }, +}; + +export function getConfig() { + return config; +} + +export function setConfig(newConfig: Partial) { + Object.assign(config, newConfig); } diff --git a/lib/bigcommerce/api/operations/get-all-products.ts b/lib/bigcommerce/api/operations/get-all-products.ts index 24b154571..c69bea34d 100644 --- a/lib/bigcommerce/api/operations/get-all-products.ts +++ b/lib/bigcommerce/api/operations/get-all-products.ts @@ -1,3 +1,10 @@ +import { + GetAllProductsQuery, + GetAllProductsQueryVariables, +} from 'lib/bigcommerce/schema'; +import { getConfig, Images, ProductImageVariables } from '..'; +import { RecursivePartial } from '../types'; + export const getAllProductsQuery = /* GraphQL */ ` query getAllProducts( $first: Int = 10 @@ -94,3 +101,46 @@ export const getAllProductsQuery = /* GraphQL */ ` } } `; + +export interface GetAllProductsResult { + products: T extends GetAllProductsQuery + ? T['site']['products']['edges'] + : unknown; +} + +export type ProductVariables = Images & + Omit; + +async function getAllProducts(opts: { + query: string; + variables?: V; +}): Promise>; + +async function getAllProducts(opts?: { + query?: string; + variables?: ProductVariables; +}): Promise>; + +async function getAllProducts({ + query = getAllProductsQuery, + variables: vars, +}: { + query?: string; + variables?: ProductVariables; +} = {}): Promise>> { + const config = getConfig(); + const variables: GetAllProductsQueryVariables = { + ...config.imageVariables, + ...vars, + }; + const data = await config.fetch>( + query, + { variables } + ); + + return { + products: data?.site?.products?.edges, + }; +} + +export default getAllProducts; diff --git a/lib/bigcommerce/api/types/index.ts b/lib/bigcommerce/api/types/index.ts new file mode 100644 index 000000000..2c0042c2e --- /dev/null +++ b/lib/bigcommerce/api/types/index.ts @@ -0,0 +1,3 @@ +export type RecursivePartial = { + [P in keyof T]?: RecursivePartial; +}; diff --git a/lib/bigcommerce/api/utils/fetch-api.ts b/lib/bigcommerce/api/utils/fetch-api.ts new file mode 100644 index 000000000..86553a833 --- /dev/null +++ b/lib/bigcommerce/api/utils/fetch-api.ts @@ -0,0 +1,27 @@ +import { CommerceAPIFetchOptions } from 'lib/commerce/api'; +import { getConfig } from '..'; + +export default async function fetchAPI( + query: string, + { variables, preview }: CommerceAPIFetchOptions = {} +): Promise { + const config = getConfig(); + const res = await fetch(config.commerceUrl + (preview ? '/preview' : ''), { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${config.apiToken}`, + }, + body: JSON.stringify({ + query, + variables, + }), + }); + + const json = await res.json(); + if (json.errors) { + console.error(json.errors); + throw new Error('Failed to fetch API'); + } + return json.data; +} diff --git a/lib/commerce-api.ts b/lib/commerce-api.ts deleted file mode 100644 index 38d5dfd66..000000000 --- a/lib/commerce-api.ts +++ /dev/null @@ -1,9 +0,0 @@ -import BigcommerceAPI from './bigcommerce/api'; - -const API_URL = process.env.NEXT_EXAMPLE_BIGCOMMERCE_STOREFRONT_API_URL!; -const API_TOKEN = process.env.NEXT_EXAMPLE_BIGCOMMERCE_STOREFRONT_API_TOKEN!; - -export const commerce = new BigcommerceAPI({ - commerceUrl: API_URL, - apiToken: API_TOKEN, -}); diff --git a/lib/commerce/api/index.ts b/lib/commerce/api/index.ts index a8978df33..1cf50bc9a 100644 --- a/lib/commerce/api/index.ts +++ b/lib/commerce/api/index.ts @@ -1,6 +1,10 @@ -export interface CommerceAPIOptions { +export interface CommerceAPIConfig { commerceUrl: string; apiToken: string; + fetch( + query: string, + queryData?: CommerceAPIFetchOptions + ): Promise; } export interface CommerceAPIFetchOptions { @@ -8,14 +12,8 @@ export interface CommerceAPIFetchOptions { preview?: boolean; } -export interface CommerceAPI { - commerceUrl: string; - apiToken: string; +// TODO: define interfaces for all the available operations - fetch( - query: string, - queryData?: CommerceAPIFetchOptions - ): Promise; - - getAllProducts(options?: { query: string }): Promise; -} +// export interface CommerceAPI { +// getAllProducts(options?: { query: string }): Promise; +// } diff --git a/pages/index.tsx b/pages/index.tsx index a78161c2f..abc67498f 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -1,9 +1,9 @@ import { GetStaticPropsContext, InferGetStaticPropsType } from 'next'; -import { commerce } from 'lib/commerce-api'; +import getAllProducts from 'lib/bigcommerce/api/operations/get-all-products'; import { Layout } from '@components/core'; export async function getStaticProps({ preview }: GetStaticPropsContext) { - const { products } = await commerce.getAllProducts(); + const { products } = await getAllProducts(); return { props: { products },