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

Sanity GROQ查询异常:Web应用返回undefined,插件查询正常

问题:GROQ查询在Sanity插件中正常,但Next.js应用返回undefined

其他GROQ查询均可正常工作,但当前查询在Web应用中返回undefined,手动在Sanity GROQ VS插件中设置$slug变量却能成功获取目标数据。

相关代码

查询函数代码

export async function getPost(slug: string): Promise<BlogPost> {
  
  console.log('fetching post data');

  const data = createClient(clientConfig).fetch(
    groq`*[_type == "blogPost" && slug.current == $slug][0]{
      ...,
      author->,
      categories[]->,
      "mainImage": mainImage.asset-> url,
      'slug': slug.current,
      title,
      description,
    }`, 
    { slug: slug },
    {cache: 'no-store'},
  )
}

Sanity客户端配置

import {createClient} from 'next-sanity';
import {apiVersion, dataset, projectId} from '../env';

const config ={
    projectId: projectId,
    dataset: dataset,
    apiVersion: apiVersion,
    useCdn: false,
};

export default config;

环境变量配置

export const apiVersion =
  process.env.NEXT_PUBLIC_SANITY_API_VERSION || '2023-09-14'

export const dataset = assertValue(
  process.env.NEXT_PUBLIC_SANITY_DATASET,
  'Missing environment variable: NEXT_PUBLIC_SANITY_DATASET'
)

export const projectId = assertValue(
  process.env.NEXT_PUBLIC_SANITY_PROJECT_ID,
  'Missing environment variable: NEXT_PUBLIC_SANITY_PROJECT_ID'
)

export const useCdn = false

function assertValue<T>(v: T | undefined, errorMessage: string): T {
  if (v === undefined) {
    throw new Error(errorMessage)
  }

  return v
}

页面组件代码

import React from 'react';
import { getPost } from '@/sanity/schemas/sanity-utils';
import Image from 'next/image';
import {BlogPost} from '@/typings';

type Props = {
    params: {post: string};
};

export default async function post({ params }: Props[]) {


    const slug = params.slug;
    const post = await getPost(slug);
    console.log(post);

  return (
    <article className='px-10 pb-28'>
        <section className='space-y-2 border border-gray-500 text-white'>
            <div className='relative min-h-56 flex flex-col md:flex-row justify-between'>
                <picture className='absolute top-0 w-full h-full opacity-10'>
                    <Image
                        className='object-cover object-center mx-auto'
                        src={post.mainImage}
                        alt={post.author.name}
                        fill
                    />
                </picture>
                <section className='p-5 bg-grey w-full'>
                    <div className='flex flex-col md:flex-row justify-between gap-y-5'>
                        <div>
                            <h1 className='text-4xl font-extrabold'>
                                {post.title}
                            </h1>

                            <p>
                                {new Date(post._createdAt).toLocaleDateString()}
                            </p>
                        </div>
                        <div className='flex items-center space-x-2'>
                            <Image
                                className='rounded-full'
                                src={post.author.image}
                                alt={post.author.name}
                                height={40}
                                width={40}
                            />
                            <div className='w-54'>
                                <h3 className='text-lg font-bold'>
                                    {post.author.name}
                                </h3>
                                <div>
                                    {/* author bio */}
                                </div>
                            </div>
                        </div>
                    </div>
                </section>
            </div>
        </section>
    </article>
  )
}

已尝试修改$slug的多种写法,调整页面Props定义,但问题仍未解决。


解决方案

1. 修复查询函数的返回值问题

getPost函数未返回fetch获取到的数据,这是返回undefined的核心原因,需在函数末尾添加返回语句:

export async function getPost(slug: string): Promise<BlogPost> {
  
  console.log('fetching post data');

  const data = createClient(clientConfig).fetch(
    groq`*[_type == "blogPost" && slug.current == $slug][0]{
      ...,
      author->,
      categories[]->,
      "mainImage": mainImage.asset-> url,
      'slug': slug.current,
      title,
      description,
    }`, 
    { slug: slug },
    {cache: 'no-store'},
  );
  
  // 必须返回获取到的数据
  return data;
}

2. 修复页面组件的Props类型与字段匹配问题

  • Props类型定义错误:Props[]应为Props(非数组类型)
  • params字段名不匹配:定义的是post,但代码中使用params.slug,需统一字段名(需和路由文件名对应,比如路由是[slug].tsx则字段为slug):
import React from 'react';
import { getPost } from '@/sanity/schemas/sanity-utils';
import Image from 'next/image';
import {BlogPost} from '@/typings';

// 匹配路由定义的动态字段名
type Props = {
    params: {slug: string};
};

// Props无需数组类型
export default async function post({ params }: Props) {
    // 字段名匹配后正确获取slug
    const slug = params.slug;
    const post = await getPost(slug);
    console.log(post);

  return (
    <article className='px-10 pb-28'>
        <section className='space-y-2 border border-gray-500 text-white'>
            <div className='relative min-h-56 flex flex-col md:flex-row justify-between'>
                <picture className='absolute top-0 w-full h-full opacity-10'>
                    <Image
                        className='object-cover object-center mx-auto'
                        src={post.mainImage}
                        alt={post.author.name}
                        fill
                    />
                </picture>
                <section className='p-5 bg-grey w-full'>
                    <div className='flex flex-col md:flex-row justify-between gap-y-5'>
                        <div>
                            <h1 className='text-4xl font-extrabold'>
                                {post.title}
                            </h1>

                            <p>
                                {new Date(post._createdAt).toLocaleDateString()}
                            </p>
                        </div>
                        <div className='flex items-center space-x-2'>
                            <Image
                                className='rounded-full'
                                src={post.author.image}
                                alt={post.author.name}
                                height={40}
                                width={40}
                            />
                            <div className='w-54'>
                                <h3 className='text-lg font-bold'>
                                    {post.author.name}
                                </h3>
                                <div>
                                    {/* author bio */}
                                </div>
                            </div>
                        </div>
                    </div>
                </section>
            </div>
        </section>
    </article>
  )
}

3. 额外检查点

  • 确认Next.js动态路由文件名正确,比如[slug].tsx,确保params.slug能获取到对应值
  • 在页面组件中打印params,确认传递的slug值是否正确:console.log('params:', params);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 21:54:51