5分钟速通GraphQL

7/15/2025 graphql

# 5 分钟速通 GraphQL

GraphQL 是一种用于 API 的查询语言,也是一个服务器端运行时,用于执行查询。它允许客户端精确获取所需数据,避免过多或过少数据传输。

# 目录


# GraphQL 简介

GraphQL 是 Facebook 推出的 API 查询语言,旨在解决 REST API 的数据冗余和不足问题。它允许客户端按需获取数据,提升前后端协作效率。

  • 特点
    • 单一 Endpoint
    • 精确查询
    • 强类型 Schema
    • 实时能力(Subscription)

# 核心概念

# Schema(模式)

Schema 定义了数据的类型、查询(Query)、变更(Mutation)等,是 GraphQL 服务的核心。

  • 类型定义:描述数据结构(如 Article、User 等)。
  • Query:定义可查询的数据入口。
  • Mutation:定义可修改数据的操作。

# 示例:定义类型和基本操作(schema.ts)

import { gql } from 'apollo-server';

const typeDefs = gql`
  type Article {
    id: ID!
    title: String!
    content: String!
    category: Category
    createdAt: String
    updatedAt: String
  }

  type Category {
    id: ID!
    name: String!
  }

  type Query {
    articles: [Article]
    article(id: ID!): Article
    categories: [Category]
    category(id: ID!): Category
  }

  type Mutation {
    createArticle(title: String!, content: String!, categoryId: ID): Article
    updateArticle(id: ID!, title: String, content: String, categoryId: ID): Article
    deleteArticle(id: ID!): Boolean
    createCategory(name: String!): Category
    updateCategory(id: ID!, name: String!): Category
    deleteCategory(id: ID!): Boolean
  }
`;

export default typeDefs;

# Resolvers(解析器)

Resolvers 是实现 Schema 中每个字段的函数,决定如何获取和返回数据。

  • Query Resolver:处理查询请求。
  • Mutation Resolver:处理数据变更。

# 示例:Mongoose 管理 Article 模型

假设你用 Mongoose 管理 Article 和 Category 模型,Resolvers 可能如下:

// Article.ts
import mongoose, { Schema, Document } from 'mongoose';

export interface IArticle extends Document {
  title: string;
  content: string;
  category: mongoose.Types.ObjectId;
  createdAt: Date;
  updatedAt: Date;
}

const ArticleSchema: Schema = new Schema(
  {
    title: { type: String, required: true },
    content: { type: String, required: true },
    category: { type: Schema.Types.ObjectId, ref: 'Category' },
  },
  { timestamps: true }
);

// Category.ts
export default mongoose.model<IArticle>('Article', ArticleSchema);

import mongoose, { Schema, Document } from 'mongoose';

export interface ICategory extends Document {
  name: string;
}

const CategorySchema: Schema = new Schema({
  name: { type: String, required: true },
});

export default mongoose.model<ICategory>('Category', CategorySchema);
import Article from './models/Article';
import Category from './models/Category';

const resolvers = {
  Query: {
    articles: async () => Article.find().populate('category'),
    article: async (_: any, { id }: { id: string }) => Article.findById(id).populate('category'),
    categories: async () => Category.find(),
    category: async (_: any, { id }: { id: string }) => Category.findById(id),
  },
  Mutation: {
    createArticle: async (_: any, { title, content, categoryId }: any) => {
      const article = new Article({ title, content, category: categoryId });
      await article.save();
      return article;
    },
    updateArticle: async (_: any, { id, title, content, categoryId }: any) => {
      return Article.findByIdAndUpdate(id, { title, content, category: categoryId }, { new: true });
    },
    deleteArticle: async (_: any, { id }: { id: string }) => {
      await Article.findByIdAndDelete(id);
      return true;
    },
    createCategory: async (_: any, { name }: { name: string }) => {
      const category = new Category({ name });
      await category.save();
      return category;
    },
    updateCategory: async (_: any, { id, name }: any) => {
      return Category.findByIdAndUpdate(id, { name }, { new: true });
    },
    deleteCategory: async (_: any, { id }: { id: string }) => {
      await Category.findByIdAndDelete(id);
      return true;
    },
  },
  Article: {
    category: async (parent: any) => {
      return Category.findById(parent.category);
    },
  },
};

export default resolvers;

# 常见面试题

1. GraphQL 和 REST 有什么区别?

  • GraphQL 允许客户端指定所需数据,避免 over-fetching/under-fetching。
  • REST 每个资源一个 endpoint,数据结构固定。

2. 什么是 Resolver?它的作用是什么?

  • Resolver 是用于处理 GraphQL 查询的函数,决定如何获取和返回数据。

3. GraphQL 如何处理 N+1 查询问题?

  • 通过 DataLoader 等工具进行批量请求和缓存,减少数据库查询次数。

# 实践建议

  • Schema 设计要简洁明了,避免过度嵌套。
  • Resolvers 要注意性能优化,如使用 DataLoader 批量处理数据库请求。
  • 错误处理要规范,返回统一的错误格式。
  • 善用工具链:如 Apollo Server、GraphQL Playground、GraphiQL。

# 参考资料