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

Next.js+StepZen GraphQL实现Reddit克隆:帖子详情页无法获取数据及变量未提供错误求助

问题分析与解决方案

先看你遇到的错误Error! Variable '$title' of required type 'String!' was not provided.,根源是变量传递不匹配,再加上Next.js路由参数的加载时机问题,还有查询返回类型的不匹配,咱们一步步解决:

1. 修正变量传递的核心问题

你的GraphQL查询需要的是$title变量,但在useQuery里你传的是id: router.query.id,这完全不对应!

解决步骤:

  • 首先确认你的路由文件名称:如果是通过slug(比如帖子标题的格式化版本)跳转,你的路由应该是pages/posts/[title].tsx(或者jsx),这时候router.query.title才是你要传的变量值。
  • 修改useQuery的变量部分:
const { data, error, loading } = useQuery(GET_POST_LIST_BY_TITLE, {
  variables: { title: router.query.title as string }, // 把id改成title,对应查询的$title变量
  skip: !router.query.title, // 当title不存在时跳过查询,避免触发错误
});

2. 处理Next.js路由参数的加载时机

在Next.js客户端渲染中,router.query初始是undefined,如果直接执行useQuery,会在参数还没准备好的时候发送请求,导致变量缺失。

优化方案:

  • 添加加载状态判断,同时用skip选项避免提前执行查询:
const PostPage = () => {
  const router = useRouter();
  const { data, error, loading } = useQuery(GET_POST_LIST_BY_TITLE, {
    variables: { title: router.query.title as string },
    skip: !router.query.title, // 没有title时不执行查询
  });

  // 路由参数还没加载完成时显示加载状态
  if (!router.isReady || loading) {
    return <div>Loading...</div>;
  }

  if (error) {
    return <div>Error! {error.message}</div>;
  }

  // 注意:查询返回的是数组,取第一个元素
  const post = data?.getPostListByPostTitle?.[0];
  
  if (!post) {
    return <div>Post not found!</div>;
  }

  return (
    <div>
      <Post post={post} />
    </div>
  );
};

3. 修正查询返回类型的不匹配

你的StepZen Schema里getPostListByPostTitle返回的是[Post]数组,但你在组件里直接把它赋值给单个Post类型的变量,这会导致类型错误和数据取不到的问题。

两种优化方式:

方式一:修改StepZen Schema返回单个Post(推荐,因为标题应该是唯一的)

// 把getPostListByPostTitle改成getPostByTitle,返回单个Post
getPostByTitle(title: String!): Post
@dbquery(
  type: "postgresql"
  query: """
  select * from "post" where title = $1 limit 1
  """
  configuration: "postgresql_config"
)

然后同步修改你的GraphQL查询语句:

export const GET_POST_BY_TITLE = gql`
  query GetPostByTitle($title: String!) {
    getPostByTitle(title: $title) {
      id
      title
      username
      body
      created_at
      image
      subreddit {
        created_at
        id
        topic
      }
      subreddit_id
      comments {
        created_at
        id
        text
      }
      votes {
        created_at
        upvote
        username
        post_id
        id
      }
    }
  }
`;

组件里就可以直接取const post = data?.getPostByTitle;,不用再取数组元素了。

方式二:保持数组返回,但组件里正确处理

如果不想改Schema,就在组件里取数组的第一个元素:

const post = data?.getPostListByPostTitle?.[0];
// 还要判断post是否存在,避免渲染空数据
if (!post) {
  return <div>Post not found!</div>;
}

4. 可选:用SSR预取数据(解决客户端加载的延迟问题)

如果想用服务端渲染(SSR)来提前获取数据,可以使用getServerSideProps:

import { gql, ApolloClient, InMemoryCache } from "@apollo/client";

export async function getServerSideProps(context) {
  const { title } = context.params;
  if (!title) {
    return {
      notFound: true,
    };
  }

  // 创建临时Apollo客户端
  const client = new ApolloClient({
    uri: "你的StepZen GraphQL端点",
    cache: new InMemoryCache(),
  });

  try {
    const { data } = await client.query({
      query: GET_POST_BY_TITLE, // 用修改后的单个Post查询
      variables: { title },
    });

    return {
      props: {
        post: data.getPostByTitle,
      },
    };
  } catch (error) {
    return {
      props: {
        error: error.message,
      },
    };
  }
}

const PostPage = ({ post, error }) => {
  if (error) {
    return <div>Error! {error}</div>;
  }

  if (!post) {
    return <div>Post not found!</div>;
  }

  return (
    <div>
      <Post post={post} />
    </div>
  );
};

export default PostPage;

最后检查点

  • 确认你的StepZen端点配置正确,数据库里确实存在对应title的帖子。
  • 确认路由参数传递正确:比如跳转时用router.push(/posts/${encodeURIComponent(post.title)}),避免特殊字符导致参数解析错误。

内容的提问来源于stack exchange,提问作者Aliyasir Naç

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 21:57:41