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

如何在Next.js中像Gatsby那样配置GraphQL及相关插件?

在Next.js中实现类Gatsby的GraphQL数据查询方案

能不能在Next.js中实现类似Gatsby的GraphQL配置?

Next.js本身没有内置Gatsby那种自动将文件转化为GraphQL可查询节点的机制,但可以通过两种方式实现类似的数据查询能力:

  1. 手动搭建GraphQL服务:在Next.js的API路由中,用GraphQL Yoga、Apollo Server等工具搭建自己的GraphQL服务,手动读取本地MDX文件、解析frontmatter,将数据暴露为GraphQL可查询的字段。
  2. 结合静态生成能力直接获取数据:如果不需要GraphQL,也可以在getStaticProps/getStaticPaths中直接用Node.js工具读取并解析MDX文件,这种方式更轻量化,也是Next.js推荐的静态站点数据获取方式。

Gatsby插件的Next.js替代方案

Gatsby的gatsby-source-filesystem、gatsby-transformer-remark等插件,在Next.js生态中可以用以下工具替代:

  • 替代gatsby-source-filesystem:用Node.js原生fs模块或glob库遍历本地文件系统,读取MDX/Markdown文件的路径和内容。
  • 替代gatsby-transformer-remark/gatsby-plugin-mdx:用gray-matter提取MDX文件的frontmatter元数据,用@next/mdx或next-mdx-remote解析MDX内容并渲染。

示例1:无GraphQL的MDX数据获取(轻量化方案)

先封装一个读取MDX文件的工具函数:

// lib/mdx.js
import fs from 'fs'
import path from 'path'
import matter from 'gray-matter'
import { serialize } from 'next-mdx-remote/serialize'

// 定义MDX文件存放目录
const MDX_DIR = path.join(process.cwd(), 'content')

// 根据slug获取单个MDX文件的元数据和内容
export async function getMdxPost(slug) {
  const filePath = path.join(MDX_DIR, `${slug}.mdx`)
  const rawContent = fs.readFileSync(filePath, 'utf-8')
  // 分离frontmatter和MDX内容
  const { data: frontmatter, content } = matter(rawContent)
  // 序列化MDX内容用于渲染
  const mdxSource = await serialize(content)

  return { frontmatter, mdxSource }
}

// 获取所有MDX文件的frontmatter元数据
export async function getAllMdxFrontmatter() {
  const fileNames = fs.readdirSync(MDX_DIR)
  return fileNames.map(fileName => {
    const slug = fileName.replace(/\.mdx$/, '')
    const filePath = path.join(MDX_DIR, fileName)
    const rawContent = fs.readFileSync(filePath, 'utf-8')
    const { data: frontmatter } = matter(rawContent)
    return { slug, ...frontmatter }
  })
}

然后在页面中使用:

// pages/posts/[slug].js
import { MDXRemote } from 'next-mdx-remote'
import { getMdxPost, getAllMdxFrontmatter } from '../../lib/mdx'

// 生成静态路径
export async function getStaticPaths() {
  const posts = await getAllMdxFrontmatter()
  return {
    paths: posts.map(post => ({ params: { slug: post.slug } })),
    fallback: false
  }
}

// 获取页面数据
export async function getStaticProps({ params }) {
  const post = await getMdxPost(params.slug)
  return { props: post }
}

// 渲染页面
export default function PostPage({ frontmatter, mdxSource }) {
  return (
    <article>
      <h1>{frontmatter.title}</h1>
      <div className="post-meta">
        <span>{frontmatter.date}</span>
        <span>{frontmatter.country}</span>
      </div>
      <div className="post-tags">
        {frontmatter.tags.map(tag => <span key={tag}>{tag}</span>)}
      </div>
      <MDXRemote {...mdxSource} />
    </article>
  )
}

示例2:手动搭建GraphQL服务实现类Gatsby查询

如果一定要用GraphQL查询,可以在Next.js中搭建GraphQL服务:
首先安装依赖:npm install graphql-yoga gray-matter
然后创建API路由:

// pages/api/graphql.js
import { createYoga, createSchema } from 'graphql-yoga'
import fs from 'fs'
import path from 'path'
import matter from 'gray-matter'

const MDX_DIR = path.join(process.cwd(), 'content')

// 定义GraphQL Schema
const schema = createSchema({
  typeDefs: `
    type Frontmatter {
      tags: [String]
      slug: String!
      title: String!
      date: String!
      country: String
      categories: [String]
    }

    type Mdx {
      frontmatter: Frontmatter!
    }

    type Query {
      # 根据slug查询单个MDX文件
      mdx(slug: String!): Mdx
      # 查询所有MDX文件的元数据
      allMdx: [Mdx]
    }
  `,
  // 实现解析器
  resolvers: {
    Query: {
      mdx: (_, { slug }) => {
        const filePath = path.join(MDX_DIR, `${slug}.mdx`)
        const rawContent = fs.readFileSync(filePath, 'utf-8')
        const { data: frontmatter } = matter(rawContent)
        return { frontmatter }
      },
      allMdx: () => {
        const fileNames = fs.readdirSync(MDX_DIR)
        return fileNames.map(fileName => {
          const slug = fileName.replace(/\.mdx$/, '')
          const filePath = path.join(MDX_DIR, fileName)
          const rawContent = fs.readFileSync(filePath, 'utf-8')
          const { data: frontmatter } = matter(rawContent)
          return { frontmatter: { ...frontmatter, slug } }
        })
      }
    }
  }
})

// 创建GraphQL Yoga实例
export default createYoga({
  schema,
  graphqlEndpoint: '/api/graphql'
})

// 禁用Next.js默认的body解析,交给GraphQL Yoga处理
export const config = {
  api: {
    bodyParser: false
  }
}

之后就可以用Apollo Client等工具在页面中发起GraphQL查询,比如:

// 先在_app.js中配置Apollo Client
import { ApolloProvider, ApolloClient, InMemoryCache } from '@apollo/client'

const client = new ApolloClient({
  uri: '/api/graphql',
  cache: new InMemoryCache()
})

export default function App({ Component, pageProps }) {
  return (
    <ApolloProvider client={client}>
      <Component {...pageProps} />
    </ApolloProvider>
  )
}

然后在页面中查询数据:

// pages/posts/[slug].js
import { useQuery, gql } from '@apollo/client'
import { useRouter } from 'next/router'

const GET_MDX_POST = gql`
  query GetMdxPost($slug: String!) {
    mdx(slug: $slug) {
      frontmatter {
        tags
        slug
        title
        date
        country
        categories
      }
    }
  }
`

export default function PostPage() {
  const router = useRouter()
  const { slug } = router.query
  const { loading, error, data } = useQuery(GET_MDX_POST, {
    variables: { slug },
    skip: !slug
  })

  if (loading) return <div>加载中...</div>
  if (error) return <div>出错了:{error.message}</div>
  if (!data?.mdx) return <div>文章不存在</div>

  const { frontmatter } = data.mdx
  return (
    <article>
      <h1>{frontmatter.title}</h1>
      {/* 渲染其他元数据和内容 */}
    </article>
  )
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 18:35:53