faststore-data-fetching

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

FastStore Data Layer & API Integration

FastStore 数据层与API集成

When this skill applies

本技能的适用场景

Use this skill when:
  • You need to fetch product data, extend existing queries with additional fields, or integrate third-party APIs.
  • You need data beyond what native FastStore components display by default.
  • You are creating API extensions in
    src/graphql/vtex/
    or
    src/graphql/thirdParty/
    .
  • You are adding GraphQL fragments in
    src/fragments/
    to include new fields in predefined queries.
  • You are writing server-side resolvers that call VTEX REST APIs or external services.
Do not use this skill for:
  • Client-side state management (cart, session, search) — use the
    faststore-state-management
    skill.
  • Visual theming — use the
    faststore-theming
    skill.
  • Component replacement or props overriding — use the
    faststore-overrides
    skill.
在以下场景中使用本技能:
  • 你需要获取商品数据、为现有查询扩展额外字段,或集成第三方API。
  • 你需要获取FastStore原生组件默认未展示的数据。
  • 你正在
    src/graphql/vtex/
    src/graphql/thirdParty/
    目录下创建API扩展。
  • 你正在
    src/fragments/
    目录下添加GraphQL片段,以便在预定义查询中包含新字段。
  • 你正在编写调用VTEX REST API或外部服务的服务端解析器。
以下场景请勿使用本技能:
  • 客户端状态管理(购物车、会话、搜索)——请使用
    faststore-state-management
    技能。
  • 视觉主题定制——请使用
    faststore-theming
    技能。
  • 组件替换或属性覆盖——请使用
    faststore-overrides
    技能。

Decision rules

决策规则

  • Use the FastStore GraphQL API for all catalog data (products, collections, search results, prices) — never make direct REST calls from client-side code.
  • Use VTEX API extensions (
    src/graphql/vtex/
    ) when accessing VTEX platform data not exposed by default (e.g., custom product fields, installment details).
  • Use third-party API extensions (
    src/graphql/thirdParty/
    ) when integrating external data sources (e.g., reviews, ratings, external inventory).
  • Use server fragments (
    src/fragments/ServerProduct.ts
    ) for data needed at page load (SSR).
  • Use client fragments (
    src/fragments/ClientProduct.ts
    ) for data that can load after initial render.
  • Keep API keys and secrets in server-side resolvers only — never in client-side code or
    NEXT_PUBLIC_
    environment variables.
  • Do not create custom Next.js API routes (
    pages/api/
    ) — use the API extension system instead.
  • 所有目录数据(商品、集合、搜索结果、价格)必须使用FastStore GraphQL API获取——绝不能从客户端代码直接调用REST接口。
  • 当访问VTEX平台默认未暴露的数据(如自定义商品字段、分期付款详情)时,使用VTEX API扩展(
    src/graphql/vtex/
    )。
  • 当集成外部数据源(如评论、评分、外部库存)时,使用第三方API扩展(
    src/graphql/thirdParty/
    )。
  • 页面加载(SSR)所需的数据使用服务端片段(
    src/fragments/ServerProduct.ts
    )。
  • 初始渲染后可加载的数据使用客户端片段(
    src/fragments/ClientProduct.ts
    )。
  • API密钥和机密信息仅能在服务端解析器中使用——绝不能出现在客户端代码或
    NEXT_PUBLIC_
    前缀的环境变量中。
  • 请勿创建自定义Next.js API路由(
    pages/api/
    )——请使用API扩展系统替代。

Hard constraints

硬性约束

Constraint: Use the GraphQL Layer for Catalog Data

约束:使用GraphQL层获取目录数据

MUST use the FastStore GraphQL API for fetching catalog data (products, collections, search results, prices). MUST NOT make direct REST calls to VTEX Catalog APIs (
/api/catalog/
,
/api/catalog_system/
) from client-side code.
Why this matters The FastStore API handles authentication, caching, request batching, and data normalization. Direct REST calls bypass all of these optimizations and expose your VTEX domain structure to the browser. They also create CORS issues, duplicate data fetching logic, and miss the type safety that GraphQL provides. Server-side REST calls to VTEX APIs are acceptable in GraphQL resolvers — that's exactly what API extensions are for.
Detection If you see
fetch('https://{account}.vtexcommercestable.com.br/api/catalog')
or
fetch('https://{account}.myvtex.com/api/catalog')
in client-side code (components, hooks, useEffect) → warn that this bypasses the GraphQL layer. If it's in a file under
src/graphql/
resolvers → this is acceptable (that's the API extension pattern). If you see
axios
or
fetch
with VTEX API paths in any file under
src/components/
or
src/pages/
→ STOP and refactor to use the GraphQL API.
Correct
typescript
// src/graphql/vtex/resolvers/product.ts
// Server-side resolver — REST calls to VTEX APIs are correct here
import type { Resolver } from '@faststore/api'

const productResolver: Record<string, Resolver> = {
  StoreProduct: {
    customAttribute: async (root, _args, context) => {
      // Server-side: safe to call VTEX REST APIs in resolvers
      const response = await context.clients.commerce.catalog.getProduct(
        root.productID
      )
      return response.customAttribute
    },
  },
}

export default productResolver
Wrong
typescript
// src/components/ProductCustomData.tsx
// WRONG: Direct REST call to VTEX Catalog API from a client component
import React, { useEffect, useState } from 'react'

interface ProductCustomDataProps {
  productId: string
}

export default function ProductCustomData({ productId }: ProductCustomDataProps) {
  const [data, setData] = useState(null)

  useEffect(() => {
    // WRONG: Direct REST call from the browser
    // This exposes the VTEX domain, bypasses caching, and creates CORS issues.
    fetch(`https://mystore.vtexcommercestable.com.br/api/catalog/pvt/product/${productId}`)
      .then((res) => res.json())
      .then(setData)
  }, [productId])

  return <div>{data?.Name}</div>
}

必须使用FastStore GraphQL API获取目录数据(商品、集合、搜索结果、价格)。绝不能从客户端代码直接调用VTEX目录API(
/api/catalog/
/api/catalog_system/
)。
重要性说明 FastStore API会处理身份验证、缓存、请求批处理和数据标准化。直接调用REST接口会绕过所有这些优化,还会将你的VTEX域名结构暴露给浏览器,同时引发CORS问题、重复数据获取逻辑,并且失去GraphQL提供的类型安全性。在GraphQL解析器中进行服务端REST调用是允许的——这正是API扩展的设计用途。
检测方式 如果你在客户端代码(组件、钩子、useEffect)中看到
fetch('https://{account}.vtexcommercestable.com.br/api/catalog')
fetch('https://{account}.myvtex.com/api/catalog')
——需警告这绕过了GraphQL层。如果该代码位于
src/graphql/
目录下的解析器文件中——则是合规的(符合API扩展模式)。如果你在
src/components/
src/pages/
目录下的任何文件中看到使用
axios
fetch
调用VTEX API路径——请立即停止并重构为使用GraphQL API。
合规示例
typescript
// src/graphql/vtex/resolvers/product.ts
// 服务端解析器——在此处调用VTEX REST API是合规的
import type { Resolver } from '@faststore/api'

const productResolver: Record<string, Resolver> = {
  StoreProduct: {
    customAttribute: async (root, _args, context) => {
      // 服务端:在解析器中调用VTEX REST API是安全的
      const response = await context.clients.commerce.catalog.getProduct(
        root.productID
      )
      return response.customAttribute
    },
  },
}

export default productResolver
错误示例
typescript
// src/components/ProductCustomData.tsx
// 错误:从客户端组件直接调用VTEX目录API
import React, { useEffect, useState } from 'react'

interface ProductCustomDataProps {
  productId: string
}

export default function ProductCustomData({ productId }: ProductCustomDataProps) {
  const [data, setData] = useState(null)

  useEffect(() => {
    // 错误:从浏览器直接调用REST接口
    // 这会暴露VTEX域名、绕过缓存并引发CORS问题。
    fetch(`https://mystore.vtexcommercestable.com.br/api/catalog/pvt/product/${productId}`)
      .then((res) => res.json())
      .then(setData)
  }, [productId])

  return <div>{data?.Name}</div>
}

Constraint: Never Expose API Keys in Client-Side Code

约束:绝不在客户端代码中暴露API密钥

MUST NOT include VTEX API keys (
VTEX_APP_KEY
,
VTEX_APP_TOKEN
) or any secret credentials in client-side code, environment variables prefixed with
NEXT_PUBLIC_
, or any file that gets bundled into the browser.
Why this matters API keys in client-side code are visible to anyone who inspects the page source or network requests. VTEX API keys provide access to catalog management, order processing, and account administration. Exposed keys can be used to modify products, access customer data, or disrupt store operations. This is a critical security vulnerability.
Detection If you see
VTEX_APP_KEY
,
VTEX_APP_TOKEN
,
X-VTEX-API-AppKey
, or
X-VTEX-API-AppToken
in any file under
src/components/
,
src/pages/
, or any file that runs in the browser → STOP immediately. This is a critical security issue. If you see
NEXT_PUBLIC_VTEX_APP_KEY
or
NEXT_PUBLIC_VTEX_APP_TOKEN
in
.env
files → STOP immediately. The
NEXT_PUBLIC_
prefix makes these values available in the browser bundle.
Correct
typescript
// src/graphql/vtex/resolvers/installments.ts
// API keys are used ONLY in server-side resolvers, accessed via context
import type { Resolver } from '@faststore/api'

const installmentResolver: Record<string, Resolver> = {
  StoreProduct: {
    availableInstallments: async (root, _args, context) => {
      // context.clients handles authentication automatically
      // No API keys are hardcoded or exposed
      const product = await context.clients.commerce.catalog.getProduct(
        root.productID
      )

      const installments = product.items?.[0]?.sellers?.[0]?.commertialOffer?.Installments || []

      return installments.map((inst: any) => ({
        count: inst.NumberOfInstallments,
        value: inst.Value,
        totalValue: inst.TotalValuePlusInterestRate,
        interestRate: inst.InterestRate,
      }))
    },
  },
}

export default installmentResolver
Wrong
typescript
// src/components/ProductInstallments.tsx
// CRITICAL SECURITY ISSUE: API keys exposed in client-side code
import React, { useEffect, useState } from 'react'

export default function ProductInstallments({ productId }: { productId: string }) {
  const [installments, setInstallments] = useState([])

  useEffect(() => {
    fetch(`https://mystore.vtexcommercestable.com.br/api/catalog/pvt/product/${productId}`, {
      headers: {
        // CRITICAL: These keys are now visible to EVERY visitor of your site.
        // Anyone can extract them from the browser's network tab.
        'X-VTEX-API-AppKey': 'vtexappkey-mystore-ABCDEF',
        'X-VTEX-API-AppToken': 'very-secret-token-12345',
      },
    })
      .then((res) => res.json())
      .then((data) => setInstallments(data.Installments))
  }, [productId])

  return <div>{installments.length} installments available</div>
}

绝不能在客户端代码、
NEXT_PUBLIC_
前缀的环境变量或任何会被打包到浏览器的文件中包含VTEX API密钥(
VTEX_APP_KEY
VTEX_APP_TOKEN
)或任何机密凭证。
重要性说明 客户端代码中的API密钥会被任何查看页面源代码或网络请求的人获取。VTEX API密钥可用于目录管理、订单处理和账户管理。暴露的密钥可能被用于修改商品、访问客户数据或破坏店铺运营,这是严重的安全漏洞。
检测方式 如果你在
src/components/
src/pages/
目录下的任何文件或任何在浏览器中运行的文件中看到
VTEX_APP_KEY
VTEX_APP_TOKEN
X-VTEX-API-AppKey
X-VTEX-API-AppToken
——请立即停止,这是严重的安全问题。如果你在
.env
文件中看到
NEXT_PUBLIC_VTEX_APP_KEY
NEXT_PUBLIC_VTEX_APP_TOKEN
——请立即停止。
NEXT_PUBLIC_
前缀会使这些值在浏览器包中可见。
合规示例
typescript
// src/graphql/vtex/resolvers/installments.ts
// API密钥仅在服务端解析器中使用,通过context访问
import type { Resolver } from '@faststore/api'

const installmentResolver: Record<string, Resolver> = {
  StoreProduct: {
    availableInstallments: async (root, _args, context) => {
      // context.clients会自动处理身份验证
      // 无需硬编码或暴露API密钥
      const product = await context.clients.commerce.catalog.getProduct(
        root.productID
      )

      const installments = product.items?.[0]?.sellers?.[0]?.commertialOffer?.Installments || []

      return installments.map((inst: any) => ({
        count: inst.NumberOfInstallments,
        value: inst.Value,
        totalValue: inst.TotalValuePlusInterestRate,
        interestRate: inst.InterestRate,
      }))
    },
  },
}

export default installmentResolver
错误示例
typescript
// src/components/ProductInstallments.tsx
// 严重安全问题:API密钥暴露在客户端代码中
import React, { useEffect, useState } from 'react'

export default function ProductInstallments({ productId }: { productId: string }) {
  const [installments, setInstallments] = useState([])

  useEffect(() => {
    fetch(`https://mystore.vtexcommercestable.com.br/api/catalog/pvt/product/${productId}`, {
      headers: {
        // 严重问题:这些密钥现在对所有网站访客可见。
        // 任何人都可以从浏览器的网络面板中提取它们。
        'X-VTEX-API-AppKey': 'vtexappkey-mystore-ABCDEF',
        'X-VTEX-API-AppToken': 'very-secret-token-12345',
      },
    })
      .then((res) => res.json())
      .then((data) => setInstallments(data.Installments))
  }, [productId])

  return <div>{installments.length} installments available</div>
}

Constraint: Follow the API Extension Directory Structure

约束:遵循API扩展目录结构

MUST place API extension files in the correct directory structure:
src/graphql/vtex/
for VTEX API extensions and
src/graphql/thirdParty/
for third-party API extensions. Each must contain
typeDefs/
and
resolvers/
subdirectories.
Why this matters FastStore's build system discovers and compiles API extensions from these specific directories. Files placed elsewhere will not be included in the GraphQL schema and resolvers will not execute. There will be no error at build time — the extended fields simply won't exist, causing runtime GraphQL errors when components try to query them.
Detection If you see GraphQL type definitions (
.graphql
files) or resolver files outside of
src/graphql/vtex/
or
src/graphql/thirdParty/
→ warn that they will not be discovered by the build system. If the
typeDefs/
or
resolvers/
subdirectory is missing → warn about incorrect structure.
Correct
graphql
undefined
必须将API扩展文件放置在正确的目录结构中:VTEX API扩展放在
src/graphql/vtex/
目录下,第三方API扩展放在
src/graphql/thirdParty/
目录下。每个目录必须包含
typeDefs/
resolvers/
子目录。
重要性说明 FastStore的构建系统会从这些特定目录中发现并编译API扩展。放置在其他位置的文件不会被包含到GraphQL模式中,解析器也不会执行。构建时不会报错,但扩展的字段将不存在,导致组件查询这些字段时出现运行时GraphQL错误。
检测方式 如果你看到GraphQL类型定义(
.graphql
文件)或解析器文件不在
src/graphql/vtex/
src/graphql/thirdParty/
目录下——需警告这些文件不会被构建系统发现。如果缺少
typeDefs/
resolvers/
子目录——需警告目录结构不正确。
合规示例
graphql
undefined

src/graphql/vtex/typeDefs/product.graphql

src/graphql/vtex/typeDefs/product.graphql

type StoreProduct { availableInstallments: [Installment] }
type Installment { count: Int value: Float totalValue: Float interestRate: Float }

```typescript
// src/graphql/vtex/resolvers/product.ts
import type { Resolver } from '@faststore/api'

const productResolver: Record<string, Resolver> = {
  StoreProduct: {
    availableInstallments: async (root, _args, context) => {
      const product = await context.clients.commerce.catalog.getProduct(
        root.productID
      )
      const installments =
        product.items?.[0]?.sellers?.[0]?.commertialOffer?.Installments || []

      return installments.map((inst: any) => ({
        count: inst.NumberOfInstallments,
        value: inst.Value,
        totalValue: inst.TotalValuePlusInterestRate,
        interestRate: inst.InterestRate,
      }))
    },
  },
}

export default productResolver
typescript
// src/graphql/vtex/resolvers/index.ts
import { default as StoreProductResolver } from './product'

const resolvers = {
  ...StoreProductResolver,
}

export default resolvers
Wrong
typescript
// WRONG: Resolver placed in src/api/ instead of src/graphql/vtex/resolvers/
// src/api/resolvers/product.ts
// This file will NOT be discovered by FastStore's build system.
// The GraphQL schema will NOT include the extended fields.
// Components querying these fields will get runtime errors.

const productResolver = {
  StoreProduct: {
    availableInstallments: async (root: any) => {
      return []
    },
  },
}

export default productResolver
type StoreProduct { availableInstallments: [Installment] }
type Installment { count: Int value: Float totalValue: Float interestRate: Float }

```typescript
// src/graphql/vtex/resolvers/product.ts
import type { Resolver } from '@faststore/api'

const productResolver: Record<string, Resolver> = {
  StoreProduct: {
    availableInstallments: async (root, _args, context) => {
      const product = await context.clients.commerce.catalog.getProduct(
        root.productID
      )
      const installments =
        product.items?.[0]?.sellers?.[0]?.commertialOffer?.Installments || []

      return installments.map((inst: any) => ({
        count: inst.NumberOfInstallments,
        value: inst.Value,
        totalValue: inst.TotalValuePlusInterestRate,
        interestRate: inst.InterestRate,
      }))
    },
  },
}

export default productResolver
typescript
// src/graphql/vtex/resolvers/index.ts
import { default as StoreProductResolver } from './product'

const resolvers = {
  ...StoreProductResolver,
}

export default resolvers
错误示例
typescript
// 错误:解析器放在src/api/而非src/graphql/vtex/resolvers/目录下
// src/api/resolvers/product.ts
// 该文件不会被FastStore的构建系统发现。
// GraphQL模式将不会包含扩展的字段。
// 查询这些字段的组件会出现运行时错误。

const productResolver = {
  StoreProduct: {
    availableInstallments: async (root: any) => {
      return []
    },
  },
}

export default productResolver

Preferred pattern

推荐模式

Recommended file layout for API extensions:
text
src/
├── graphql/
│   ├── vtex/
│   │   ├── typeDefs/
│   │   │   └── product.graphql
│   │   └── resolvers/
│   │       ├── product.ts
│   │       └── index.ts
│   └── thirdParty/
│       ├── typeDefs/
│       │   └── extra.graphql
│       └── resolvers/
│           ├── reviews.ts
│           └── index.ts
└── fragments/
    ├── ServerProduct.ts    ← server-side fragment (SSR)
    └── ClientProduct.ts    ← client-side fragment (post-render)
Minimal API extension — add a field to
StoreProduct
:
graphql
undefined
API扩展的推荐文件布局:
text
src/
├── graphql/
│   ├── vtex/
│   │   ├── typeDefs/
│   │   │   └── product.graphql
│   │   └── resolvers/
│   │       ├── product.ts
│   │       └── index.ts
│   └── thirdParty/
│       ├── typeDefs/
│       │   └── extra.graphql
│       └── resolvers/
│           ├── reviews.ts
│           └── index.ts
└── fragments/
    ├── ServerProduct.ts    ← 服务端片段(SSR)
    └── ClientProduct.ts    ← 客户端片段(渲染后加载)
最小化API扩展——为
StoreProduct
添加字段:
graphql
undefined

src/graphql/vtex/typeDefs/product.graphql

src/graphql/vtex/typeDefs/product.graphql

type StoreProduct { availableInstallments: [Installment] }
type Installment { count: Int! value: Float! totalValue: Float! interestRate: Float! }

```typescript
// src/graphql/vtex/resolvers/product.ts
import type { Resolver } from '@faststore/api'

const productResolver: Record<string, Resolver> = {
  StoreProduct: {
    availableInstallments: async (root, _args, context) => {
      const product = await context.clients.commerce.catalog.getProduct(
        root.productID
      )
      const installments =
        product.items?.[0]?.sellers?.[0]?.commertialOffer?.Installments || []

      return installments.map((inst: any) => ({
        count: inst.NumberOfInstallments,
        value: inst.Value,
        totalValue: inst.TotalValuePlusInterestRate,
        interestRate: inst.InterestRate,
      }))
    },
  },
}

export default productResolver
Include the new field in queries via fragments:
typescript
// src/fragments/ServerProduct.ts
import { gql } from '@faststore/core/api'

export const fragment = gql(`
  fragment ServerProduct on Query {
    product(locator: $locator) {
      availableInstallments {
        count
        value
        totalValue
        interestRate
      }
    }
  }
`)
Third-party API extension (e.g., product reviews):
typescript
// src/graphql/thirdParty/resolvers/reviews.ts
import type { Resolver } from '@faststore/api'

const REVIEWS_API_KEY = process.env.REVIEWS_API_KEY // Server-only env var (no NEXT_PUBLIC_ prefix)

const reviewsResolver: Record<string, Resolver> = {
  StoreProduct: {
    reviews: async (root) => {
      const response = await fetch(
        `https://api.reviews-service.com/products/${root.productID}/reviews`,
        {
          headers: {
            Authorization: `Bearer ${REVIEWS_API_KEY}`,
          },
        }
      )
      const data = await response.json()
      return {
        averageRating: data.average_rating,
        totalReviews: data.total_count,
        reviews: data.reviews.slice(0, 5).map((r: any) => ({
          author: r.author_name,
          rating: r.rating,
          text: r.review_text,
          date: r.created_at,
        })),
      }
    },
  },
}

export default reviewsResolver
type StoreProduct { availableInstallments: [Installment] }
type Installment { count: Int! value: Float! totalValue: Float! interestRate: Float! }

```typescript
// src/graphql/vtex/resolvers/product.ts
import type { Resolver } from '@faststore/api'

const productResolver: Record<string, Resolver> = {
  StoreProduct: {
    availableInstallments: async (root, _args, context) => {
      const product = await context.clients.commerce.catalog.getProduct(
        root.productID
      )
      const installments =
        product.items?.[0]?.sellers?.[0]?.commertialOffer?.Installments || []

      return installments.map((inst: any) => ({
        count: inst.NumberOfInstallments,
        value: inst.Value,
        totalValue: inst.TotalValuePlusInterestRate,
        interestRate: inst.InterestRate,
      }))
    },
  },
}

export default productResolver
通过片段在查询中包含新字段:
typescript
// src/fragments/ServerProduct.ts
import { gql } from '@faststore/core/api'

export const fragment = gql(`
  fragment ServerProduct on Query {
    product(locator: $locator) {
      availableInstallments {
        count
        value
        totalValue
        interestRate
      }
    }
  }
`)
第三方API扩展示例(如商品评论):
typescript
// src/graphql/thirdParty/resolvers/reviews.ts
import type { Resolver } from '@faststore/api'

const REVIEWS_API_KEY = process.env.REVIEWS_API_KEY // 仅服务端环境变量(无NEXT_PUBLIC_前缀)

const reviewsResolver: Record<string, Resolver> = {
  StoreProduct: {
    reviews: async (root) => {
      const response = await fetch(
        `https://api.reviews-service.com/products/${root.productID}/reviews`,
        {
          headers: {
            Authorization: `Bearer ${REVIEWS_API_KEY}`,
          },
        }
      )
      const data = await response.json()
      return {
        averageRating: data.average_rating,
        totalReviews: data.total_count,
        reviews: data.reviews.slice(0, 5).map((r: any) => ({
          author: r.author_name,
          rating: r.rating,
          text: r.review_text,
          date: r.created_at,
        })),
      }
    },
  },
}

export default reviewsResolver

Common failure modes

常见失败模式

  • Making direct REST calls to VTEX Catalog APIs from React components — creates CORS issues, bypasses caching, and exposes VTEX account structure to the browser.
  • Exposing API keys (
    VTEX_APP_KEY
    ,
    VTEX_APP_TOKEN
    ) in client-side code or
    NEXT_PUBLIC_
    environment variables — critical security vulnerability.
  • Placing resolvers or type definitions outside
    src/graphql/vtex/
    or
    src/graphql/thirdParty/
    — they will not be discovered by the build system.
  • Creating custom Next.js API routes (
    pages/api/
    ) instead of using the API extension system — bypasses caching, type safety, and request batching.
  • Forgetting to create the resolver index file (
    src/graphql/vtex/resolvers/index.ts
    ) that re-exports all resolvers.
  • 从React组件直接调用VTEX目录API的REST接口——引发CORS问题、绕过缓存并向浏览器暴露VTEX账户结构。
  • 在客户端代码或
    NEXT_PUBLIC_
    环境变量中暴露API密钥(
    VTEX_APP_KEY
    VTEX_APP_TOKEN
    )——严重安全漏洞。
  • 将解析器或类型定义放在
    src/graphql/vtex/
    src/graphql/thirdParty/
    目录之外——不会被构建系统发现。
  • 创建自定义Next.js API路由(
    pages/api/
    )而非使用API扩展系统——绕过缓存、类型安全性和请求批处理。
  • 忘记创建重新导出所有解析器的解析器索引文件(
    src/graphql/vtex/resolvers/index.ts
    )。

Review checklist

审核清单

  • Is all catalog data fetched via the FastStore GraphQL API (not direct REST calls from components)?
  • Are API extension files in
    src/graphql/vtex/
    or
    src/graphql/thirdParty/
    with proper
    typeDefs/
    and
    resolvers/
    subdirectories?
  • Does the resolver index file re-export all resolvers?
  • Are API keys and secrets used only in server-side resolvers (no
    NEXT_PUBLIC_
    prefix)?
  • Are fragments created in
    src/fragments/
    to include new fields in predefined queries?
  • Are there no custom Next.js API routes that could be replaced with API extensions?
  • 所有目录数据是否都通过FastStore GraphQL API获取(而非从组件直接调用REST接口)?
  • API扩展文件是否放在
    src/graphql/vtex/
    src/graphql/thirdParty/
    目录下,且包含正确的
    typeDefs/
    resolvers/
    子目录?
  • 解析器索引文件是否重新导出了所有解析器?
  • API密钥和机密信息是否仅在服务端解析器中使用(无
    NEXT_PUBLIC_
    前缀)?
  • 是否在
    src/fragments/
    目录下创建了片段以在预定义查询中包含新字段?
  • 是否存在可被API扩展替代的自定义Next.js API路由?

Reference

参考资料