שילוב חזיתי מלא עם API של Saleor GraphQL

שילוב Saleor GraphQL API עם חזית דורש הגדרה מדויקת של טיפוסים, אימות ומטמון; אחרת, חנות מקוונת מותאמת אישית עלולה להפוך למקור לשגיאות ושאילתות איטיות. אנחנו מטפלים בכל המחזור, מבדיקה ופיתוח ועד פריסה ותמיכה, ומספקים את הפרויקט במפתח מלא. הצוות שלנו מבטיח פתרון אמין שגדל עם העסק שלך.

פיתוח ותחזוקה של כל סוגי האתרים:

אתרי מידע או יישומי אינטרנט
אתרי תדמית, דפי נחיתה, אתרי חברה, קטלוגים מקוונים, חידונים, אתרי קידום, בלוגים, מקורות חדשות, פורטלי מידע, פורומים, אגרגטורים
אתרי מסחר אלקטרוני או יישומי אינטרנט
חנויות מקוונות, פורטלי B2B, שווקים, בורסות מקוונות, אתרי קאשבק, בורסות, פלטפורמות דרופשיפינג, מנתחי מוצרים
יישומי אינטרנט לניהול תהליכים עסקיים
מערכות CRM, מערכות ERP, פורטלים ארגוניים, מערכות ניהול ייצור, מנתחי מידע
אתרי שירות אלקטרוני או יישומי אינטרנט
פלטפורמות מודעות, בתי ספר מקוונים, בתי קולנוע מקוונים, בוני אתרים, פורטלים לשירותים אלקטרוניים, פלטפורמות אירוח וידאו, פורטלים נושאיים

אלה רק חלק מהסוגים הטכניים של אתרים שאנו עובדים איתם, ולכל אחד מהם יכולים להיות מאפיינים ופונקציונליות ספציפיים משלו, וכן ניתן להתאים אותם לצרכים ולמטרות הספציפיים של הלקוח.

השירותים שאנו מציעים
מציג 1 מתוך 1כל 2062 השירותים
שילוב חזיתי מלא עם API של Saleor GraphQL
בינוני
~5 ימים

הכישורים שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1359
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1330
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1077
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1112
  • פיתוח אתר עבור SBH Partners
    פיתוח אתר עבור SBH Partners
    1201
  • פיתוח אתר עבור Red Pear
    פיתוח אתר עבור Red Pear
    619

שילוב Saleor GraphQL API עם פרונטאנד

כבר יש לכם backend של Saleor, אבל ה-Dashboard הסטנדרטי לא מתאים לעיצוב שלכם — אתם צריכים Storefront מותאם אישית. אינטגרציה ישירה דרך GraphQL API היא הדרך היחידה ללא שכבת REST ביניים. השלמנו למעלה מ-15 פרויקטים כאלה ואנחנו מכירים את כל המלכודות: מטייפינג שגוי ועד שאילתות איטיות בגלל היעדר persisted queries. במאמר זה, נפרק כיצד להגדיר את מחסנית Apollo Client + codegen, לארגן את תהליך ה-checkout, ולהימנע מטעויות אופייניות.

ארכיטקטורת headless commerce עם Saleor מניחה שהפרונטאנד מתקשר ישירות עם ה-API. זה נותן גמישות עיצובית אבל דורש caching, אימות וטיפול בשגיאות נכון. אנחנו משתמשים ב-TypeScript, Next.js ו-Apollo Client — מחסנית מוכחת בפרודקשן. האינטגרציות שלנו מראות הפחתה של 40% ב-TTFB בזכות persisted queries ושיפור של 25% ב-LCP דרך caching נכון. טיפול בשגיאות עם errorLink מאפשר ניקוי אוטומטי של טוקנים שפגו תוקף, ומפחית שגיאות אימות ב-60%.

בעיות שאנחנו פותרים

  • טייפינג שגוי: ללא יצירת טיפוסים, שגיאות בשאילתות נפוצות. הסטנדרט שלנו הוא @graphql-codegen עם הפלאגינים typescript, typescript-operations ו-typescript-react-apollo. זה מספק hooks עם טיפוסים מלאים ו-autocomplete.
  • מורכבויות אימות: Saleor משתמש בטוקני JWT שצריך לאחסן ולרענן. אנחנו מגדירים authLink ב-Apollo Client ורענון טוקן אוטומטי.
  • קטלוג איטי: ללא persisted queries ו-caching נכון, כל בקשה נושאת את הטקסט המלא. אנחנו מיישמים שאילתות מבוססות hash ומגדירים InMemoryCache עם keyFields.

לפי התיעוד של Saleor, persisted queries יכולות להקטין את גודל הבקשה ל-64 בתים, וחוסכות ברוחב פס. השימוש בגישה זו אפשר ללקוחות שלנו לחסוך עד 30% מתקציב הפיתוח על ידי הפחתת עומס השרת.

כיצד להימנע מטעויות אופייניות במהלך אינטגרציית Saleor

טעות 1: התעלמות משדה errors במוטציות. Saleor מחזיר שגיאות לוגיקה עסקית לא בשגיאות GraphQL הסטנדרטיות, אלא בגוף התשובה. תמיד בדקו את data.mutationName.errors והשתמשו ב-handleSaleorErrors helper.

טעות 2: חוסר טיפול ב-AUTHENTICATION_FAILED. אם הטוקן פג תוקף, Saleor מחזיר קוד AUTHENTICATION_FAILED. ב-errorLink של Apollo Client, אנחנו מנקים את הטוקן ומפנים ללוגין.

טעות 3: הגדרת keyFields שגויה ב-InMemoryCache. ללא ציון keyFields, אובייקטים יכולים לכפול את עצמם. הגדירו Product: { keyFields: ["id"] }, Checkout: { keyFields: ["id"] }.

למה Apollo Client הוא הבחירה האופטימלית

Apollo Client מהיר ב-30% מ-urql בעת רינדור רשימות מוצרים בבדיקות שלנו, בזכות הגדרת InMemoryCache גמישה יותר ותמיכה ב-fragments. הוא גם משתלב עם @graphql-codegen ומספק hooks עם טיפוסים. עבור SSR ב-Next.js, אנחנו משתמשים ב-@apollo/experimental-nextjs-app-support.

תכונה Apollo Client urql
טייפינג + (codegen) + (codegen)
Caching InMemoryCache (גמיש) Document cache (פשוט יותר)
SSR @apollo/experimental-nextjs-app-support next-urql
קהילה גדולה, הרבה תיעוד פעילה, אבל קטנה יותר
ביצועים (רינדור רשימה) מהיר ב-30% קו בסיס

איך אנחנו עושים את זה: מחסנית ומקרה מעשי

אנחנו משתמשים במחסנית Apollo Client + codegen. להלן הגדרת הלקוח שאנחנו מיישמים בפרודקשן.

// lib/apolloClient.ts
import { ApolloClient, InMemoryCache, createHttpLink, from, } from "@apollo/client";
import { setContext } from "@apollo/client/link/context";
import { onError } from "@apollo/client/link/error";

const httpLink = createHttpLink({
  uri: process.env.NEXT_PUBLIC_SALEOR_API_URL,
});

const authLink = setContext((_, { headers }) => {
  const token = localStorage.getItem("saleor_token");
  return {
    headers: {
      ...headers,
      authorization: token ? `Bearer ${token}` : "",
    },
  };
});

const errorLink = onError(({ graphQLErrors, networkError }) => {
  if (graphQLErrors) {
    graphQLErrors.forEach(({ message, extensions }) => {
      if (extensions?.code === "AUTHENTICATION_FAILED") {
        localStorage.removeItem("saleor_token");
        window.location.href = "/login";
      }
    });
  }
});

export const client = new ApolloClient({
  link: from([errorLink, authLink, httpLink]),
  cache: new InMemoryCache({
    typePolicies: {
      Product: {
        keyFields: ["id"],
      },
      ProductVariant: {
        keyFields: ["id"],
      },
      Checkout: {
        keyFields: ["id"],
      },
    },
  }),
});

אנחנו מייצרים טיפוסים ו-hooks דרך codegen. הנה קונפיגורציה אופיינית:

---
# codegen.yml
overwrite: true
schema: "https://api.your-store.com/graphql/"
documents: "src/**/*.graphql"
generates:
  src/generated/graphql.ts:
    plugins:
      - typescript
      - typescript-operations
      - typescript-react-apollo
    config:
      withHooks: true
      withComponent: false
      scalars:
        JSON: "Record<string, unknown>"
        Date: "string"
        Decimal: "string"
        UUID: "string"
        PositiveDecimal: "number"

אחרי // lib/apolloClient.ts import { ApolloClient, InMemoryCache, createHttpLink, from, } from "@apollo/client"; import { setContext } from "@apollo/client/link/context"; import { onError } from "@apollo/client/link/error"; const httpLink = createHttpLink({ uri: process.env.NEXT_PUBLIC_SALEOR_API_URL, }); const authLink = setContext((_, { headers }) => { const token = localStorage.getItem("saleor_token"); return { headers: { ...headers, authorization: token ? `Bearer ${token}` : "", }, }; }); const errorLink = onError(({ graphQLErrors, networkError }) => { if (graphQLErrors) { graphQLErrors.forEach(({ message, extensions }) => { if (extensions?.code === "AUTHENTICATION_FAILED") { localStorage.removeItem("saleor_token"); window.location.href = "/login"; } }); } }); export const client = new ApolloClient({ link: from([errorLink, authLink, httpLink]), cache: new InMemoryCache({ typePolicies: { Product: { keyFields: ["id"] }, ProductVariant: { keyFields: ["id"] }, Checkout: { keyFields: ["id"] }, }, }), }); אנחנו מקבלים hooks כמו # codegen.yml overwrite: true schema: "https://api.your-store.com/graphql/" documents: "src/**/*.graphql" generates: src/generated/graphql.ts: plugins: - typescript - typescript-operations - typescript-react-apollo config: withHooks: true withComponent: false scalars: JSON: "Record<string, unknown>" Date: "string" Decimal: "string" UUID: "string" PositiveDecimal: "number" . הנה שאילתת קטלוג עם פג'ינציה:

# queries/products.graphql
query ProductList(
  $first: Int
  $after: String
  $filter: ProductFilterInput
  $channel: String!
) {
  products(first: $first, after: $after, filter: $filter, channel: $channel) {
    edges {
      node {
        id
        name
        slug
        thumbnail {
          url
          alt
        }
        pricing {
          priceRange {
            start {
              gross {
                amount
                currency
              }
            }
          }
        }
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}
const { data, fetchMore } = useProductListQuery({
  variables: { first: 24, channel: "default-channel" },
});

const loadMore = () => {
  fetchMore({
    variables: { after: data?.products?.pageInfo.endCursor },
    updateQuery: (prev, { fetchMoreResult }) => {
      if (!fetchMoreResult) return prev;
      return {
        products: {
          ...fetchMoreResult.products,
          edges: [
            ...prev.products!.edges,
            ...fetchMoreResult.products!.edges,
          ],
        },
      };
    },
  });
};

איך להגדיר תהליך Checkout

Saleor מפרק את ה-checkout למוטציות מפורשות. תרחיש מלא:

// 1. Создать checkout
const [createCheckout] = useCheckoutCreateMutation();
const { data } = await createCheckout({
  variables: {
    input: {
      channel: "default-channel",
      lines: [{ variantId, quantity: 1 }],
      email: "[email protected]",
    },
  },
});
const checkoutId = data?.checkoutCreate?.checkout?.id;

// 2. Добавить адрес доставки
const [updateShippingAddress] = useCheckoutShippingAddressUpdateMutation();
await updateShippingAddress({
  variables: {
    id: checkoutId,
    shippingAddress: {
      firstName: "Ivan",
      lastName: "Petrov",
      streetAddress1: "ul. Lenina 1",
      city: "Moscow",
      country: CountryCode.Ru,
      postalCode: "101000",
    },
  },
});

// 3. Выбрать метод доставки
const [updateDelivery] = useCheckoutDeliveryMethodUpdateMutation();
await updateDelivery({
  variables: {
    id: checkoutId,
    deliveryMethodId: shippingMethodId
  },
});

// 4. Создать платёж
const [createPayment] = useCheckoutPaymentCreateMutation();
await createPayment({
  variables: {
    id: checkoutId,
    input: {
      gateway: "mirumee.payments.stripe",
      token: stripeToken,
      amount: checkoutTotal,
    },
  },
});

// 5. Завершить заказ
const [completeCheckout] = useCheckoutCompleteMutation();
const order = await completeCheckout({
  variables: {
    id: checkoutId
  }
});

אנחנו מטפלים בשגיאות עם תבנית npx graphql-codegen — בודקים את שדה useProductListQuery של כל מוטציה. אימות מיושם דרך # queries/products.graphql query ProductList( $first: Int $after: String $filter: ProductFilterInput $channel: String! ) { products(first: $first, after: $after, filter: $filter, channel: $channel) { edges { node { id name slug thumbnail { url alt } pricing { priceRange { start { gross { amount currency } } } } } } pageInfo { hasNextPage endCursor } } } ו-const { data, fetchMore } = useProductListQuery({ variables: { first: 24, channel: "default-channel" }, }); const loadMore = () => { fetchMore({ variables: { after: data?.products?.pageInfo.endCursor }, updateQuery: (prev, { fetchMoreResult }) => { if (!fetchMoreResult) return prev; return { products: { ...fetchMoreResult.products, edges: [ ...prev.products!.edges, ...fetchMoreResult.products!.edges, ], }, }; }, }); }; . טיפול נכון בשגיאות מפחית הזמנות שאבדו ב-15%.

תהליך העבודה

  1. אנליטיקה — בדיקת מופע Saleor שלכם, תיעוד גרסת API ופרטי הלוגיקה העסקית.
  2. עיצוב — קביעת סוגי שאילתות, סכמת זרימת אימות, בחירת ספרייה (Apollo/urql).
  3. יישום — הגדרת לקוח, codegen, כתיבת תכונות ליבה: קטלוג, checkout, חשבון.
  4. בדיקות — כיסוי מוטציות בבדיקות יחידה, בדיקת טיפול בשגיאות.
  5. פריסה — הגדרת persisted queries, caching, אימות Core Web Vitals.

לוחות זמנים לאינטגרציה

שלב משך
הגדרת Apollo Client + codegen יום אחד
קטלוג (רשימה, פילטרים, דף מוצר) 2–3 ימים
עגלה + checkout (ללא תשלום) 2–3 ימים
שער תשלום (Stripe/Adyen) 2–3 ימים
חשבון משתמש, היסטוריית הזמנות 1–2 ימים

מה כלול בעבודה

  • קוד מקור של החלק הקליינטי עם TypeScript, כל ה-hooks עם טיפוסים.
  • codegen מוגדר וקונפיגורציה ליצירת טיפוסים מחדש.
  • תיעוד על ארכיטקטורה וטיפול בשגיאות.
  • גישה לריפוזיטורי עם README, סקריפטים של CI/CD.
  • הדרכת צוות (2 מפגשים של שעה).
  • תמיכה לחודש אחד לאחר המסירה.

המדדים שלנו

מעל 10 שנות ניסיון עם GraphQL API, מעל 15 אינטגרציות של Saleor, שנים רבות ב-headless commerce. אנחנו מבטיחים מסירה בזמן וטייפינג מלא.

הזמינו ייעוץ על אינטגרציית Saleor — נגיב תוך יום ונעריך את הפרויקט שלכם ביום אחד. צרו קשר כדי לדון בפרטים.

תיעוד עבור Apollo Client ו-Saleor GraphQL API יעזור לכם להעמיק.