1186 字
6 分钟
GraphQL 实战指南:从 Schema 设计到性能优化的完整教程

GraphQL 实战指南:从 Schema 设计到性能优化的完整教程#

🚀 前言:GraphQL 是一种用于 API 的查询语言,它允许客户端精确地声明需要的数据,避免了 REST API 的过度获取和获取不足问题。这篇文章将带你从入门到实战,掌握 GraphQL 开发。


一、GraphQL 核心概念#

1.1 与 REST 的对比#

REST API 的问题:

  • 过度获取:获取了不需要的字段
  • 获取不足:需要多次请求才能获取完整数据
  • 版本管理:API 版本迭代困难

GraphQL 的优势:

  • 精确获取:只获取需要的字段
  • 一次请求:通过嵌套查询获取关联数据
  • 强类型:Schema 定义了严格的类型系统
  • 无需版本:通过 Schema 演进

1.2 Schema 定义#

# 定义类型
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
}
type Post {
id: ID!
title: String!
content: String!
author: User!
createdAt: String!
}
# 定义查询
type Query {
user(id: ID!): User
users: [User!]!
post(id: ID!): Post
posts(authorId: ID): [Post!]!
}
# 定义变更
type Mutation {
createUser(input: CreateUserInput!): User!
updateUser(id: ID!, input: UpdateUserInput!): User!
deleteUser(id: ID!): Boolean!
createPost(input: CreatePostInput!): Post!
}
# 输入类型
input CreateUserInput {
name: String!
email: String!
}
input UpdateUserInput {
name: String
email: String
}
input CreatePostInput {
title: String!
content: String!
authorId: ID!
}

1.3 查询语法#

# 基本查询
query {
user(id: "1") {
id
name
email
}
}
# 嵌套查询
query {
user(id: "1") {
name
posts {
title
createdAt
}
}
}
# 带参数的查询
query GetUser($id: ID!) {
user(id: $id) {
name
email
}
}
# 变更
mutation CreateUser($input: CreateUserInput!) {
createUser(input: $input) {
id
name
email
}
}
# 片段复用
fragment UserFields on User {
id
name
email
}
query {
user(id: "1") {
...UserFields
posts {
title
}
}
}

二、后端实现#

2.1 Apollo Server#

import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
// 定义 Schema
const typeDefs = `#graphql
type Book {
title: String
author: String
}
type Query {
books: [Book]
}
`;
// 模拟数据
const books = [
{ title: 'The Awakening', author: 'Kate Chopin' },
{ title: 'City of Glass', author: 'Paul Auster' }
];
// 定义 Resolvers
const resolvers = {
Query: {
books: () => books,
},
};
// 创建服务器
const server = new ApolloServer({
typeDefs,
resolvers,
});
const { url } = await startStandaloneServer(server, {
listen: { port: 4000 },
});
console.log(`Server ready at: ${url}`);

2.2 复杂 Resolver#

const resolvers = {
Query: {
user: async (_, { id }, { dataSources }) => {
return dataSources.userAPI.getUser(id);
},
users: async (_, __, { dataSources }) => {
return dataSources.userAPI.getUsers();
}
},
User: {
// 为 User 类型的 posts 字段提供解析
posts: async (parent, _, { dataSources }) => {
return dataSources.postAPI.getPostsByAuthor(parent.id);
},
// 计算字段
fullName: (parent) => `${parent.firstName} ${parent.lastName}`
},
Mutation: {
createPost: async (_, { input }, { dataSources, user }) => {
if (!user) throw new AuthenticationError('Not authenticated');
return dataSources.postAPI.createPost({
...input,
authorId: user.id
});
}
}
};

2.3 DataLoader 解决 N+1 问题#

import DataLoader from 'dataloader';
// 批量加载函数
const batchUsers = async (ids) => {
const users = await db.users.findMany({
where: { id: { in: ids } }
});
// 按原始顺序返回
return ids.map(id => users.find(user => user.id === id));
};
// 创建 DataLoader
const userLoader = new DataLoader(batchUsers);
// 在 Resolver 中使用
const resolvers = {
Post: {
author: (post, _, { loaders }) => {
return loaders.user.load(post.authorId);
}
}
};
// 上下文
const context = () => ({
loaders: {
user: new DataLoader(batchUsers)
}
});

三、前端使用 Apollo Client#

3.1 客户端配置#

import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
const httpLink = createHttpLink({
uri: 'http://localhost:4000/graphql',
});
const authLink = setContext((_, { headers }) => {
const token = localStorage.getItem('token');
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : '',
}
};
});
const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache({
typePolicies: {
Query: {
fields: {
users: {
merge(existing, incoming) {
return incoming;
}
}
}
}
}
})
});

3.2 Vue 中使用#

<script setup>
import { useQuery, useMutation } from '@vue/apollo-composable';
import gql from 'graphql-tag';
// 查询
const { result, loading, error } = useQuery(gql`
query GetUsers {
users {
id
name
email
}
}
`);
// 变更
const { mutate: createUser, onDone } = useMutation(gql`
mutation CreateUser($input: CreateUserInput!) {
createUser(input: $input) {
id
name
}
}
`);
const handleCreate = async () => {
await createUser({
input: { name: '张三', email: 'zhangsan@example.com' }
});
};
</script>

四、性能优化#

4.1 查询复杂度限制#

import { createComplexityLimitRule } from 'graphql-validation-complexity';
const complexityLimit = createComplexityLimitRule(1000);
const server = new ApolloServer({
typeDefs,
resolvers,
validationRules: [complexityLimit]
});

4.2 持久化查询#

// 使用 Apollo Persisted Queries
import { createPersistedQueryLink } from '@apollo/client/link/persisted-queries';
import { sha256 } from 'crypto-hash';
const link = createPersistedQueryLink({
sha256,
useGETForHashedQueries: true
});

4.3 缓存策略#

const cache = new InMemoryCache({
typePolicies: {
Post: {
fields: {
comments: {
merge(existing = [], incoming) {
return [...existing, ...incoming];
}
}
}
}
}
});

五、订阅(Subscription)#

// Schema
type Subscription {
postAdded: Post
}
// Resolver
import { PubSub } from 'graphql-subscriptions';
const pubsub = new PubSub();
const resolvers = {
Subscription: {
postAdded: {
subscribe: () => pubsub.asyncIterator(['POST_ADDED'])
}
},
Mutation: {
createPost: async (_, { input }) => {
const post = await db.posts.create(input);
pubsub.publish('POST_ADDED', { postAdded: post });
return post;
}
}
};

六、总结#

GraphQL 带来了 API 开发的新范式:

  1. 精确数据获取:客户端决定需要什么数据
  2. 强类型系统:Schema 定义了严格的类型
  3. 单一端点:所有操作通过一个端点完成
  4. 强大的工具链:Apollo、Relay 等完善的生态
  5. 实时更新:Subscription 支持实时数据推送

GraphQL 特别适合复杂的数据关系场景,如社交网络、电商系统等。掌握 GraphQL 将帮助你构建更灵活、更高效的 API!

GraphQL 实战指南:从 Schema 设计到性能优化的完整教程
https://www.oferry.com/posts/a83/
作者
晨平安
发布于
2026-02-27
许可协议
CC BY-NC-SA 4.0
封面
示例歌曲
示例艺术家
封面
示例歌曲
示例艺术家
0:00 / 0:00