You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Remix React中基于GraphQL实现Shopify Storefront搜索组件

在Remix中实现搜索功能的两种方案

对于你的问题,查询可以直接在客户端组件中处理,也可以通过Remix的服务端路由来实现(更推荐后者,尤其是API需要密钥等敏感信息时),下面是具体实现方式:


方案一:客户端直接发起GraphQL请求(适合公开API)

如果你的GraphQL API无需敏感验证,可以直接在handleSearch中用fetch发送请求:

async function handleSearch() {
  if (!searchTerm.trim()) return;
  
  try {
    const response = await fetch('你的GraphQL API地址', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        query: SEARCH_PRODUCTS_QUERY,
        variables: { query: searchTerm }
      })
    });
    
    const data = await response.json();
    if (data.data?.products?.edges) {
      setSearchResults(data.data.products.edges);
    }
  } catch (error) {
    console.error('搜索失败:', error);
  }
}

方案二:用Remix服务端Action处理(推荐,更安全)

如果API需要密钥或敏感信息,必须通过服务端路由转发请求,避免客户端暴露敏感数据:

1. 创建服务端路由处理搜索请求

在app/routes下新建api.search.tsx(或api/search.ts):

import { json } from "@remix-run/node";

export async function action({ request }: { request: Request }) {
  const formData = await request.formData();
  const searchTerm = formData.get('query') as string;
  
  if (!searchTerm.trim()) {
    return json({ products: [] }, { status: 400 });
  }

  // 服务端调用GraphQL API,可安全使用密钥
  const response = await fetch('你的GraphQL API地址', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer 你的API密钥' // 按需添加
    },
    body: JSON.stringify({
      query: `
        query SearchProducts($query: String!) {
          products(query: $query, first: 10) {
            edges {
              node {
                id
                title
                handle
                description
              }
            }
          }
        }
      `,
      variables: { query: searchTerm }
    })
  });
  
  const data = await response.json();
  return json({ products: data.data?.products?.edges || [] });
}

2. 修改SearchForm组件调用服务端路由

import { useState } from "react";

function SearchForm() {
  const [searchTerm, setSearchTerm] = useState('');
  const [searchResults, setSearchResults] = useState([]);
  const [isLoading, setIsLoading] = useState(false);

  async function handleSearch() {
    if (!searchTerm.trim()) return;
    setIsLoading(true);
    
    try {
      const response = await fetch('/api/search', {
        method: 'POST',
        body: new FormData().append('query', searchTerm)
      });
      
      const data = await response.json();
      setSearchResults(data.products);
    } catch (error) {
      console.error('搜索失败:', error);
    } finally {
      setIsLoading(false);
    }
  }

  return (
    <div>
      <input
        type="text"
        placeholder="search"
        value={searchTerm}
        onChange={(event) => setSearchTerm(event.target.value)}
      />
      <button type="button" onClick={handleSearch} disabled={isLoading}>
        {isLoading ? '搜索中...' : '搜索'}
      </button>
      <ul>
        {searchResults.map((product) => (
          <li key={product.node.id}>{product.node.title}</li>
        ))}
      </ul>
    </div>
  );
}

总结

  • 公开API可直接在客户端处理请求;
  • 涉及敏感信息的API必须通过Remix服务端路由转发,保障安全;
  • Remix推荐用服务端处理数据请求,便于统一控制缓存、错误逻辑和权限验证。

内容的提问来源于stack exchange,提问作者avocado

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 05:27:36