🌐 《GraphQL in Next.js 初体验》中文笔记

🧩 一、概览:Next.js + GraphQL 是怎么"对话"的?

Next.js 是前后端一体框架,而 GraphQL 是一种 API 查询语言。结合起来后:

🤝 Next.js 提供运行环境

🧠 GraphQL 提供数据语义接口

简单理解:

  • Next.js = 舞台和播放器(API 路径、页面渲染、Serverless 运行)
  • GraphQL = 剧本(Schema定义)、演员台词逻辑(Resolver)、导演策略(Context)

结构大致如下:

scss 复制代码
Next.js API Route ─┬─► ApolloServer / Yoga Server
                    │
                    └─► GraphQL Schema (定义数据结构)
                        └─► Resolver (具体处理逻辑)
                            └─► Context (跨请求共享上下文)

🧱 二、Schema:GraphQL 的"数据契约"

Schema 是 定义数据形状的语言模型

可以把它理解为数据库表结构 + API 文档的结合体。

示例 ⚙️

bash 复制代码
# ./graphql/schema.graphql
type User {
  id: ID!
  name: String!
  age: Int
}

type Query {
  users: [User!]!
  user(id: ID!): User
}

type Mutation {
  addUser(name: String!, age: Int): User
}

📘 要点笔记:

  • type:定义类型(用户、文章、评论等)
  • Query:读取数据的入口
  • Mutation:修改、创建、删除数据的入口

Schema 就像一份菜单,它定义了服务员能做的所有菜,但不做菜。

做菜的是 Resolver 👉


🧠 三、Resolver:GraphQL 的"大脑中枢"

Resolver 是 Schema 的执行器 ,它负责将查询请求映射到实际数据源

在 Next.js 中,一般写成 JS/TS 文件与 Schema 匹配。

示例 👇

javascript 复制代码
// ./graphql/resolvers.js
const users = [
  { id: "1", name: "Neo", age: 29 },
  { id: "2", name: "Trinity", age: 27 },
];

export const resolvers = {
  Query: {
    users: () => users,
    user: (_, { id }) => users.find(u => u.id === id),
  },
  Mutation: {
    addUser: (_, { name, age }) => {
      const newUser = { id: String(users.length + 1), name, age };
      users.push(newUser);
      return newUser;
    },
  },
};

📘 要点笔记:

  • 每个字段对应一个函数。
  • 第一个参数 _ 通常是父级字段(这里未使用,可省略)。
  • 第二个参数 { id } 是客户端传入的变量。
  • Resolver 内不管 Schema 长啥样,它只需返回对应数据。

🌍 四、Context:连接"每个请求"的神经系统

Context 是 GraphQL 在请求周期中共享的环境对象

它的作用类似于:

  • 注入依赖(数据库实例、token 验证、用户状态)
  • 跨 Resolver 共享状态

示例:

javascript 复制代码
import jwt from "jsonwebtoken";
import db from "./db.js";

export const createContext = ({ req }) => {
  const token = req.headers.authorization || "";
  let user = null;

  try {
    user = jwt.verify(token, process.env.JWT_SECRET);
  } catch (e) {
    console.log("token 无效或未提供");
  }

  return { db, user };
};

在 Resolver 中,就能这样访问:

javascript 复制代码
Mutation: {
  addUser: (_, { name, age }, { db, user }) => {
    if (!user) throw new Error("未授权");
    return db.insertUser({ name, age });
  }
}

📘 要点笔记:

  • Context 在每次请求开始时创建(一次请求一次 Context)。
  • 经 Context 可安全访问外部资源且隔离状态。
  • 在 SSR 和 Serverless 模式的 Next.js 中非常实用。

⚙️ 五、在 Next.js 中整合 Apollo Server

在 Next.js 中最常见的方式是用 /api/graphql 作为后端入口。

javascript 复制代码
// ./pages/api/graphql.js
import { ApolloServer } from "apollo-server-micro";
import { typeDefs } from "../../graphql/schema.js";
import { resolvers } from "../../graphql/resolvers.js";
import { createContext } from "../../graphql/context.js";

const server = new ApolloServer({
  typeDefs,
  resolvers,
  context: createContext,
});

export const config = {
  api: { bodyParser: false },
};

export default server.createHandler({ path: "/api/graphql" });

💡 运行逻辑:

  1. 浏览器访问 /api/graphql
  2. Next.js 调用 ApolloServer 处理请求
  3. ApolloServer 根据 Schema 调用对应 Resolver
  4. Resolver 通过 Context 访问数据源
  5. 返回 JSON 响应

🖥️ 六、Next.js 前端调用示例

javascript 复制代码
import { gql, useQuery } from "@apollo/client";

const ALL_USERS = gql`
  query {
    users {
      id
      name
      age
    }
  }
`;

export default function UsersList() {
  const { loading, error, data } = useQuery(ALL_USERS);
  if (loading) return <p>⏳ 加载中...</p>;
  if (error) return <p>❌ 出错啦: {error.message}</p>;
  return (
    <ul>
      {data.users.map(u => (
        <li key={u.id}>
          👤 {u.name}(年龄:{u.age ?? "未知"})
        </li>
      ))}
    </ul>
  );
}

在这里,前端只需声明「我想要什么」,

后端就帮你搞定「怎么算出来」。


💬 七、小结:三大组件一图流

arduino 复制代码
<div style="max-width:720px;margin:auto;text-align:center;">
<svg width="100%" height="300" viewBox="0 0 720 300" xmlns="http://www.w3.org/2000/svg">
  <rect x="60" y="60" width="170" height="60" rx="10" fill="#A1C4FD" stroke="#333"/>
  <text x="145" y="95" text-anchor="middle" font-size="13">Schema (定义契约)</text>

  <rect x="280" y="60" width="170" height="60" rx="10" fill="#FDD692" stroke="#333"/>
  <text x="365" y="95" text-anchor="middle" font-size="13">Resolver (处理逻辑)</text>

  <rect x="500" y="60" width="170" height="60" rx="10" fill="#C2E9FB" stroke="#333"/>
  <text x="585" y="95" text-anchor="middle" font-size="13">Context (执行环境)</text>

  <line x1="230" y1="90" x2="280" y2="90" stroke="#000" stroke-width="2" marker-end="url(#arrow)"/>
  <line x1="450" y1="90" x2="500" y2="90" stroke="#000" stroke-width="2" marker-end="url(#arrow)"/>

  <defs>
    <marker id="arrow" markerWidth="10" markerHeight="10" refX="6" refY="3" orient="auto">
      <path d="M0,0 L0,6 L9,3 z" fill="#333"/>
    </marker>
  </defs>
</svg>
<p style="font-size:13px;color:#666;">▲ Next.js GraphQL 三件套结构关系图</p>
</div>

🚀 八、为什么 Next.js 是 GraphQL 的理想宿主?

  1. Serverless Ready:API 路由天然适合部署 Apollo 或 Yoga Server。
  2. SSR + CSR 混合渲染:可直连 GraphQL 数据,从后端直出页面。
  3. Edge Runtime:未来的 Web AGI 场景(边缘智能体)可直接调用 GraphQL 层。
  4. TypeScript 一体化支持:Schema + Resolver 均能生成类型定义,开发丝滑。
相关推荐
JarvanMo2 小时前
我的app被工信部下架了,现在想重新上架
前端
景早2 小时前
小黑记账清单案例(axios,echarts,vue)
前端·vue.js·echarts
我穿棉裤了2 小时前
使用css 给div添加四角线框
前端·css
爱吃烤鸡翅的酸菜鱼2 小时前
深度解析《AI+Java编程入门》:一本为零基础重构的Java学习路径
java·人工智能·后端·ai
林太白2 小时前
rust12-路由接口
后端·rust
Mintopia2 小时前
🤖 通用人工智能(AGI)离 Web 应用还有多远?
前端·javascript·aigc
SimonKing2 小时前
被卖的Hutool出AI模块了!它如何让Java调用大模型变得如此简单?
java·后端·程序员
JianZhen✓3 小时前
面试题名词解析一
前端
会跑的葫芦怪3 小时前
Web3开发中的前端、后端与合约:角色定位与协作逻辑
前端·web3·区块链