Aufbau von Headless Shops mit WooGraphQL: Kapitel 2 von 5

banner

Heads up: Dieses Tutorial deckt eine frühere WooGraphQL-Version ab und ist nicht mehr auf dem neuesten Stand. Eine neu geschriebene Version ist in Arbeit – wir werden von hier aus darauf verlinken, sobald sie veröffentlicht wurde.

https://www.youtube.com/embed/nhlexP-KtSo

Inhaltsverzeichnis

  1. Einrichten von WordPress für Ihre Headless eCommerce-Anwendung
  2. Erstellen und Einrichten Ihrer eCommerce-App für die Entwicklung
  3. Erstellen von Shop Listing Pages mit WooGraphQL
  4. Erstellen von Benutzer-Login und Navigation
  5. Erstellen der Produktseite und Warenkorboptionen

Willkommen zum zweiten Teil unserer Tutorial-Serie zum Erstellen einer Headless-E-Commerce-Anwendung! In diesem nächsten Kapitel wird die E-Commerce-App so eingerichtet, dass sie mit Ihrem Shop verbunden ist. Wir werden Next.js für das Frontend nutzen und mehrere andere Tools wie shadcn/ui für Bauteile und GraphQL Code Generator für die Handhabung von GraphQL-Operationen.

Erstellen und Einrichten unserer E-Commerce-App für die Entwicklung

Nachdem Sie Ihren WooCommerce-Shop mit WooGraphQL eingerichtet haben, sollten Sie einen GraphQL-Endpunkt bei https://site-name/graphqlWenn Sie das gebündelte Entwicklungssetup aus dem letzten Kapitel verwenden, ist dies http://localhost:8080/wp/graphql. Lassen Sie uns damit beginnen.

Next.js-Anwendung erstellen

Beginnen wir mit der Erstellung unserer Next.js-Anwendung und dem Einrichten einiger notwendiger Konfigurationen

1. Generieren Sie zunächst eine neue Next.js-Anwendung, indem Sie den folgenden Befehl in Ihrem Terminal ausführen: npx create-next-app@latest und folgen Sie den Anweisungen und navigieren Sie zum neu erstellten Projektverzeichnis.

2. Dann installieren Sie die dotenv-flow und deepmerge npm-Pakete, indem Sie den folgenden Befehl in Ihrem Terminal ausführen: npm i dotenv-flow deepmerge

3. Erstellen Sie eine neue .env.local Datei im Stammverzeichnis Ihres Projekts und füllen Sie es mit den folgenden Details:

GRAPHQL_ENDPOINT=[GRAPHQL_ENDPOINT]
FRONTEND_URL=[NEXT_APP_URL]
BACKEND_URL=[WP_URL]
SITE_NAME=[SITE_NAME]
SITE_DESCRIPTION=[SITE_DESCRIPTION]

Vergessen Sie nicht, die Klammerwerte durch Ihre jeweiligen Äquivalente zu ersetzen.

4. Erstellen Sie eine neue next.config.js Datei im Stammverzeichnis Ihres Projekts und fügen Sie folgenden Code hinzu:

/**
 * @type {import('next').NextConfig}
 */
const nextConfig = {
    reactStrictMode: true,
    swcMinify: true,
    images: {
        dangerouslyAllowSVG: true,
        formats: ['image/avif', 'image/webp'],
        domains: ['localhost'],
        minimumCacheTTL: 60,
        disableStaticImages: true,
    },
    env: {
        GRAPHQL_ENDPOINT: process.env.GRAPHQL_ENDPOINT,
        FRONTEND_URL: process.env.FRONTEND_URL,
        BACKEND_URL: process.env.BACKEND_URL,
        SITE_NAME: process.env.SITE_NAME,
        SITE_DESCRIPTION: process.env.SITE_DESCRIPTION,
    },
}

module.exports = nextConfig;

5. Aktualisierung der content Eigentum in Ihrem tailwind.config.js Datei an:

...
content: [
    './ui/**/*.{ts,tsx}',
    './server/**/*.{ts,tsx}',
    './client/**/*.{ts,tsx}',
    './app/**/*.{ts,tsx}',
]
...

Installieren shadcn/ui Komponenten

Wir werden die shadcn/ui Bibliothek für die UI-Komponenten unserer Anwendung. Hier ist, wie man es installiert und konfiguriert.

1. Führen Sie den Befehl shadcn-ui init aus: npx shadcn-ui@latest init Folgen Sie dann den Aufforderungen.

2. Öffnen Sie die components.json Datei, die generiert wurde und deren Inhalt durch Folgendes ersetzt:

{
    "$schema": "https://ui.shadcn.com/schema.json",
    "style": "new-york",
    "rsc": true,
    "tailwind": {
        "config": "tailwind.config.js",
        "css": "app/globals.css",
        "baseColor": "slate",
        "cssVariables": true
    },
    "aliases": {
        "components": "/",
        "utils": "@/utils/ui"
    }
}

3. Umbenennung lib/utils.ts Datei /utils/ui.ts und schaffen eine /ui Verzeichnis.

4. Fügen Sie die erforderlichen Komponenten zu Ihrem Projekt hinzu, indem Sie die folgenden Befehle in Ihrem Terminal ausführen:

npx shadcn-ui@latest add aspect-ratio
npx shadcn-ui@latest add badge
npx shadcn-ui@latest add button
npx shadcn-ui@latest add card
npx shadcn-ui@latest add form
npx shadcn-ui@latest add input
npx shadcn-ui@latest add label
npx shadcn-ui@latest add radio-group
npx shadcn-ui@latest add select
npx shadcn-ui@latest add sheet
npx shadcn-ui@latest add slider
npx shadcn-ui@latest add tabs
npx shadcn-ui@latest add toast

Erstellen anderer UI-Komponenten

Wir werden einige zusätzliche Komponenten für unsere Anwendung erstellen.

1. Erstellen Sie eine neue ui/Image/index.ts Datei und fügen Sie folgenden Code hinzu:

    'use client';
    export * from './Image';

2. Erstellen Sie eine neue ui/Image/Image.tsx Datei und fügen Sie folgenden Code hinzu:

import { useState } from 'react';
import NextImage from 'next/image';
import { cn } from '@/utils/ui';
import { LoadingSpinner } from '@/ui/LoadingSpinner';
import { AspectRatio } from "@/ui/aspect-ratio"

export type ImageProps = {
    className?: string;
    src: string;
    sizes?: string;
    width?: number;
    height?: number;
    ratio?: number;
    alt: string;
    style?: JSX.IntrinsicElements['img']['style']
    fill?: boolean;
    priority?: boolean;
}

export function Image(props: ImageProps) {
    const [isLoading, setLoading] = useState(true);

    const {
        className = '',
        src,
        alt,
        sizes,
        width,
        height,
        ratio,
        style,
        fill = true,
        priority,
    } = props;

    return (
        <div
            className={cn(
                'overflow-hidden group relative',
                className && className,
            )}
            style={style}
>
            <AspectRatio ratio={ratio}>
                {isLoading && (
                    <LoadingSpinner className="position absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2"/>
                )}
                <NextImage
                    src={src}
                    alt={alt}
                    width={width as number}
                    height={height as number}
                    sizes={sizes}
                    className={cn(
                        'group-hover:opacity-75 object-cover',
                        'duration-700 ease-in-out',
                        isLoading
                        ? 'grayscale blur-2xl scale-110'
                        : 'grayscale-0 blur-0 scale-100',
                    )}
                    fill={fill}
                    onLoadingComplete={() => setLoading(false)}
                    priority={priority}
/>
            </AspectRatio>
        </div>
    );
}

3. Erstellen Sie eine neue ui/LoadingSpinner/index.ts Datei und fügen Sie folgenden Code hinzu:

    export * from './LoadingSpinner';

4. Erstellen Sie eine neue ui/LoadingSpinner/LoadingSpinner.tsx Datei und fügen Sie folgenden Code hinzu:

export interface LoadingSpinnerProps {
    className?: string;
    noText?: boolean;
    color?: string;
}

export function LoadingSpinner({ className = '', noText = false, color = 'amber-400' }: LoadingSpinnerProps) {
    return (
        <div aria-label="Loading..." role="status" className={`relative flex items-center justify-center space-x-2 ${className}`}>
        <svg className="h-6 w-6 animate-spin stroke-amber-600" viewBox="0 0 256 256">
            <line x1="128" y1="32" x2="128" y2="64" strokeLinecap="round" strokeLinejoin="round" strokeWidth="24"/>
            <line
                x1="195.9"
                y1="60.1"
                x2="173.3"
                y2="82.7"
                strokeLinecap="round"
                strokeLinejoin="round"
                strokeWidth="24"
/>
            <line x1="224" y1="128" x2="192" y2="128" strokeLinecap="round" strokeLinejoin="round" strokeWidth="24"/>
            <line
                x1="195.9"
                y1="195.9"
                x2="173.3"
                y2="173.3"
                strokeLinecap="round"
                strokeLinejoin="round"
                strokeWidth="24"
/>
            <line x1="128" y1="224" x2="128" y2="192" strokeLinecap="round" strokeLinejoin="round" strokeWidth="24"/>
            <line
                x1="60.1"
                y1="195.9"
                x2="82.7"
                y2="173.3"
                strokeLinecap="round"
                strokeLinejoin="round"
                strokeWidth="24"
/>
            <line x1="32" y1="128" x2="64" y2="128" strokeLinecap="round" strokeLinejoin="round" strokeWidth="24"/>
            <line
                x1="60.1"
                y1="60.1"
                x2="82.7"
                y2="82.7"
                strokeLinecap="round"
                strokeLinejoin="round"
                strokeWidth="24"
/>
        </svg>
            {!noText && <span className="text-xs font-medium text-gray-500">Loading...</span>}
        </div>
    );
}

5. Erstellen Sie eine neue ui/NavLink/index.ts Datei und fügen Sie folgenden Code hinzu:

export * from './NavLink';

6. Erstellen Sie eine neue ui/NavLink/NavLink.tsx Datei und fügen Sie folgenden Code hinzu:

import { PropsWithChildren } from 'react';
import Link from 'next/link';
import { cn } from '@woographql/utils/ui';

export const linkClassName = 'transition-colors group-hover:text-blue-400';

export interface NavLinkProps {
    href: string;
    className?: string;
    shallow?: boolean;
    prefetch?: boolean;
}

export function NavLink(props: PropsWithChildren<NavLinkProps>) {
    const {
        children,
        href,
        className,
        shallow,
        prefetch,
    } = props;
    return (
        <Link
            className={cn(
                className,
                linkClassName,
            )}
            href={href}
            shallow={shallow}
            prefetch={prefetch}
>
            {children}
        </Link>
    );
}

Installieren und Konfigurieren von GraphQL Code Generator

Schließlich müssen wir GraphQL Code Generator einrichten (Codegen) zum Erzeugen typisierter GraphQL-Operationen. Codegen ist echte Zeitersparnis und ein Muss, wenn man mit vielen Komponenten oder einem großen GraphQL-Schema arbeitet. Je nachdem, wie es konfiguriert ist, generiert es Code, der sich auf den bereitgestellten GraphQL-Endpunkt bezieht. In unserer Anwendung wird es verwendet, um TypeScript-Typen für den GraphQL-Endpunkt, TS-Typen für die GraphQL-Operationen, die wir schreiben, und einen ordentlichen Wrapper zu generieren, der das Ausführen dieser Operationen mit dem graphql-request Bibliothek.

1. Installieren Sie den GraphQL Code Generator zusammen mit einigen erforderlichen Plugins, indem Sie den folgenden Befehl in Ihrem Terminal ausführen:

npm i -D npm-run-all @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-operations @graphql-codegen/typescript-graphql-request

2. Erstellen Sie eine neue codegen.ts Datei im Stammverzeichnis Ihres Projekts und fügen Sie folgenden Code hinzu:

import type { CodegenConfig } from '@graphql-codegen/cli';
import dotenv from 'dotenv-flow';

dotenv.config({ silent: true }); 
const config: CodegenConfig = {
    schema: process.env.GRAPHQL_ENDPOINT,
    documents: ['graphql/**/*.graphql'],
    verbose: true,
    overwrite: true,
    generates: {
        'graphql/generated.ts': {
            plugins: [
                'typescript',
                'typescript-operations',
                'typescript-graphql-request',
            ],
            config: {
                namingConvention: 'keep',
            }
        }
    }
}
export default config;

3. Aktualisierung der scripts Eigentum in Ihrem package.json Datei zum Einschließen von Skripten zum Erzeugen der GraphQL-Operationen und Wrapper:

{
    ...
    "scripts": {
        "next:dev": "next dev",
        "next:build": "next build",
        "start": "next start",
        "lint": "next lint",
        "codegen:dev": "graphql-codegen --config codegen.ts --watch",
        "codegen:build": "graphql-codegen --config codegen.ts",
        "dev": "run-p codegen:dev next:dev",
        "build": "NODE_ENV=production run-s codegen:build next:build",
    },
    ...
}

4. Erstellen Sie eine neue graphql/index.ts Datei und fügen Sie folgenden Code hinzu:

 export * from './generated';
 export * from './client';

5. Erstellen Sie eine neue graphql/main.graphql Datei und fügen Sie folgenden Code hinzu:

fragment MenuItemContent on MenuItem {
  id
  uri
  title
  label
  cssClasses
}

fragment MenuItemRecursive on MenuItem {
  ...MenuItemContent
  childItems {
    nodes {
      ...MenuItemContent
    }
  }
}

fragment MenuContent on Menu {
  id
  name
  locations
  slug
  menuItems(first: 20, where: {parentId: 0}) {
    nodes {
      ...MenuItemRecursive
    }
  }
}

fragment CustomerContent on Customer {
  id
  sessionToken
  firstName
  shipping {
    postcode
    state
    city
    country
  }
}

fragment ProductContentSlice on Product {
  id
  databaseId
  name
  slug
  type
  image {
    id
    sourceUrl(size:WOOCOMMERCE_THUMBNAIL)
    altText
  }
  ... on SimpleProduct {
    price
    regularPrice
    soldIndividually
  }
  ... on VariableProduct {
    price
    regularPrice
    soldIndividually
  }
}

fragment ProductVariationContentSlice on ProductVariation {
  id
  databaseId
  name
  slug
  image {
    id
    sourceUrl(size:WOOCOMMERCE_THUMBNAIL)
    altText
  }
  price
  regularPrice
}

fragment ProductContentSmall on Product {
  id
  databaseId
  slug
  name
  type
  shortDescription(format: RAW)
  image {
    id
    sourceUrl(size:WOOCOMMERCE_THUMBNAIL)
    altText
  }
  productCategories(first: 20) {
    nodes {
      id
      slug
      name
    }
  }
  productTags(first: 20) {
    nodes {
      id
      slug
      name
    }
  }
  allPaColor(first: 100) {
    nodes {
      id
      slug
      name
    }
  }
  ... on SimpleProduct {
    onSale
    stockStatus
    price
    rawPrice: price(format: RAW)
    regularPrice
    salePrice
    soldIndividually
  }
  ... on VariableProduct {
    onSale
    stockStatus
    price
    rawPrice: price(format: RAW)
    regularPrice
    salePrice
    soldIndividually
  }
}

fragment ProductContentFull on Product {
  id
  databaseId
  slug
  name
  type
  description
  shortDescription(format: RAW)
  image {
    id
    sourceUrl
    altText
  }
  galleryImages {
    nodes {
      id
      sourceUrl(size:WOOCOMMERCE_THUMBNAIL)
      altText
    }
  }
  productTags(first: 20) {
    nodes {
      id
      slug
      name
    }
  }
  attributes {
    nodes {
      id
      attributeId
      ... on LocalProductAttribute {
        name
        label
        options
        variation
      }
      ... on GlobalProductAttribute {
        name
        label
        options
        variation
        slug
        terms(first: 100) {
          nodes {
            id
            name
            slug
          }
        }
      }
    }
  }
  ... on SimpleProduct {
    onSale
    stockStatus
    price
    rawPrice: price(format: RAW)
    regularPrice
    salePrice
    stockStatus
    stockQuantity
    soldIndividually
    defaultAttributes(first: 100) {
      nodes {
        id
        attributeId
        name
        value
        label
      }
    }
  }
  ... on VariableProduct {
    onSale
    price
    rawPrice: price(format: RAW)
    regularPrice
    salePrice
    stockStatus
    stockQuantity
    soldIndividually
    defaultAttributes(first: 100) {
      nodes {
        id
        attributeId
        label
        name
        value
      }
    }
    variations(first: 50) {
      nodes {
        id
        databaseId
        name
        price
        rawPrice: price(format: RAW)
        regularPrice
        salePrice
        onSale
        attributes {
          nodes {
            name
            label
            value
          }
        }
        image {
          id
          sourceUrl
          altText
        }
      }
    }
  }
}

fragment VariationContent on ProductVariation {
  id
  name
  slug
  price
  regularPrice
  salePrice
  stockStatus
  stockQuantity
  onSale
  image {
    id
    sourceUrl
    altText
  }
}

fragment CartItemContent on CartItem {
  key
  product {
    node {
      ...ProductContentSlice
    }
  }
  variation {
    attributes {
      id
      label
      name
      value
    }
    node {
      ...ProductVariationContentSlice
    }
  }
  quantity
  total
  subtotal
  subtotalTax
  extraData {
    key
    value
  }
}

fragment CartContent on Cart {
  contents(first: 100) {
    itemCount
    nodes {
      ...CartItemContent
    }
  }
  appliedCoupons {
    code
    discountAmount
    discountTax
  }
  needsShippingAddress
  availableShippingMethods {
    packageDetails
    supportsShippingCalculator
    rates {
      id
      instanceId
      methodId
      label
      cost
    }
  }
  subtotal
  subtotalTax
  shippingTax
  shippingTotal
  total
  totalTax
  feeTax
  feeTotal
  discountTax
  discountTotal
}

fragment CustomerFields on Customer {
    id
    databaseId
    firstName
    lastName
    displayName
    email
    sessionToken
    metaData {
      key
      value
    }
}

query GetTopNav {
  menu(id: "primary", idType: LOCATION) {
    ...MenuContent
  }
}

query GetProducts($first: Int, $after: String, $where: RootQueryToProductConnectionWhereArgs) {
  products(first: $first, after: $after, where: $where) {
    pageInfo {
      endCursor
      hasNextPage
    }
    edges {
      cursor
      node {
        ...ProductContentSmall
      }
    }
    nodes {
      ...ProductContentSmall
    }
  }
}

query GetProduct($id: ID!, $idType: ProductIdTypeEnum) {
  product(id: $id, idType: $idType) {
    ...ProductContentFull
  }
}

query GetShopCategories($first: Int, $after: String, $where: RootQueryToProductCategoryConnectionWhereArgs) {
  productCategories(first: $first, after: $after, where: $where) {
    pageInfo {
      hasNextPage
      endCursor
    }
    edges {
      node {
        id
        name
        slug
      }
    }
    nodes {
      id
      name
      slug
    }
  }
}

query GetShopTags($first: Int, $after: String, $where: RootQueryToProductTagConnectionWhereArgs) {
  productTags(first: $first, after: $after, where: $where) {
    pageInfo {
      hasNextPage
      endCursor
    }
    edges {
      node {
        id
        name
        slug
      }
    }
    nodes {
      id
      name
      slug
    }
  }
}

query GetShopColors($first: Int, $after: String, $where: RootQueryToPaColorConnectionWhereArgs) {
  allPaColor(first: $first, after: $after, where: $where) {
    pageInfo {
      hasNextPage
      endCursor
    }
    edges {
      node {
        id
        name
        slug
      }
    }
    nodes {
      id
      name
      slug
    }
  }
}

query GetSession {
  cart {
    ...CartContent
  }
  customer {
    ...CustomerContent
  }
}

mutation AddToCart(
  $productId: Int!,
  $variationId: Int,
  $quantity: Int,
  $variation: [ProductAttributeInput],
  $extraData: String
) {
  addToCart(
    input: {
      productId: $productId,
      variationId: $variationId,
      quantity: $quantity,
      variation: $variation,
      extraData: $extraData
    }
  ) {
    cart {
      ...CartContent
    }
    cartItem {
      ...CartItemContent
    }
  }
}

mutation UpdateCartItemQuantities($items: [CartItemQuantityInput]) {
  updateItemQuantities(input: {items: $items}) {
    cart {
      ...CartContent
    }
    items {
      ...CartItemContent
    }
  }
}

mutation RemoveItemsFromCart($keys: [ID], $all: Boolean) {
  removeItemsFromCart(input: {keys: $keys, all: $all}) {
    cart {
      ...CartContent
    }
    cartItems {
      ...CartItemContent
    }
  }
}

mutation Login($username: String!, $password: String!) {
  login(input: { username: $username, password: $password }) {
    authToken
    refreshToken
    customer {
      ...CustomerFields
    }
  }
}

mutation RefreshAuthToken($refreshToken: String!) {
  refreshJwtAuthToken(input: { jwtRefreshToken: $refreshToken }) {
    authToken
  }
}

mutation UpdateSession($input: UpdateSessionInput!) {
  updateSession(input: $input) {
    session {
      id
      key
      value
    }
  }
}

6. Erstellen Sie eine neue graphql/client.ts Datei und fügen Sie folgenden Code hinzu:

import { GraphQLClient } from 'graphql-request';
import deepmerge from 'deepmerge';

import {
    RootQueryToProductConnectionWhereArgs,
    Product,
    ProductCategory,
    PaColor,
    getSdk,
    RootQueryToProductCategoryConnectionWhereArgs,
    RootQueryToPaColorConnectionWhereArgs,
    ProductIdTypeEnum,
} from './generated';

let client: GraphQLClient;
export function getClient() {
    const endpoint = process.env.GRAPHQL_ENDPOINT
    if (!endpoint) {
        throw new Error('GRAPHQL_ENDPOINT is not defined')
    }

    if (!client) {
        client = new GraphQLClient(endpoint);
    }

    return client;
}

export function getClientWithSdk() {
    return getSdk(getClient());
}

const initialConnectionResults = {
    pageInfo: {
        hasNextPage: true,
        endCursor: null,
    },
    edges: [],
    nodes: [],
};

export async function fetchProducts(
    pageSize: number, 
    pageLimit = 0,
    where?: RootQueryToProductConnectionWhereArgs
) {
    try {
        const client = getClientWithSdk();
        let data = { products: initialConnectionResults }
        let after = '';
        let count = 0
        while(data.products.pageInfo.hasNextPage && (pageLimit === 0 || count < pageLimit)) {
            const next = await client.GetProducts({
                first: pageSize,
                after,
                where,
            });

            data = deepmerge(data, next);
            after = next.products?.pageInfo.endCursor || '';
            count++;
        }

        return (data.products.nodes) as Product[];
    } catch (err) {
        console.error(err || 'Failed to fetch product listing!!!');
    }
}

export async function fetchCategories(
    pageSize: number,
    pageLimit = 0,
    where?: RootQueryToProductCategoryConnectionWhereArgs
) {
    try {
        const client = getClientWithSdk();
        let data = { productCategories: initialConnectionResults }
        let after = '';
        let count = 0
        while(data.productCategories.pageInfo.hasNextPage && (pageLimit === 0 || count < pageLimit)) {
        const next = await client.GetShopCategories({
            first: pageSize,
            after,
            where,
        });

            data = deepmerge(data, next);
            after = next.productCategories?.pageInfo.endCursor || '';
            count++;
        }

        return (data.productCategories.nodes) as ProductCategory[];

    } catch (err) {
        console.error(err || 'Failed to fetch product categories!!!');
    }
}

export async function fetchColors(
    pageSize: number,
    pageLimit = 0,
    where?: RootQueryToPaColorConnectionWhereArgs
) {
    try {
        const client = getClientWithSdk();
        let data = { allPaColor: initialConnectionResults }
        let after = '';
        let count = 0
        while(data.allPaColor.pageInfo.hasNextPage && (pageLimit === 0 || count < pageLimit)) {
        const next = await client.GetShopColors({
            first: pageSize,
            after,
            where
        });

            data = deepmerge(data, next);
            after = next.allPaColor?.pageInfo.endCursor || '';
            count++;
        }

        return (data.allPaColor.nodes) as PaColor[];
    } catch (err) {
        console.error(err || 'Failed to fetch product color attributes!!!');
    }
}

export async function fetchProductBy(slug: string, idType: ProductIdTypeEnum) {
    try {
        const client = getClientWithSdk();
        const data = await client.GetProduct({
            id: slug,
            idType: idType,
        });

        if (!data.product) {
            throw new Error('Product not found!!!');
        }

        return data.product as Product;
    } catch (err) {
        console.error(err || 'Failed to fetch product data!!!');
    }
}

Schlussfolgerung

Herzlichen Glückwunsch! Sie haben erfolgreich eine Next.js-Anwendung für die E-Commerce-Entwicklung mit dem GraphQL-Endpunkt aus Ihrem WooCommerce-Shop eingerichtet. Sie haben installiert und konfiguriert shadcn/ui Komponenten, andere UI-Komponenten erstellt und den GraphQL Code Generator installiert und konfiguriert. Ihre Anwendung ist jetzt bereit für die weitere Entwicklung und Integration mit Ihrem WooCommerce-Shop mit GraphQL.

Weiter zum nächsten Kapitel



Leave a Reply

Your email address will not be published. Required fields are marked *