GraphQL 入门教程 — 现代API查询语言

适用人群:后端开发者、前端开发者
学习时长:约1-2天
工具:Apollo Server / Express GraphQL
适用场景:复杂数据查询、移动端API

一、GraphQL是什么?

GraphQL是Facebook开发的API查询语言,客户端可以精确请求需要的数据。

对比项RESTGraphQL
端点多个端点单个端点
数据固定结构按需获取
版本URL版本控制无需版本
过度获取常见不存在
不足获取常见不存在

二、基础概念

2.1 Schema定义

# schema.graphql

# 类型定义
type User {
  id: ID!
  username: String!
  email: String!
  age: Int
  posts: [Post!]
  createdAt: String!
}

type Post {
  id: ID!
  title: String!
  content: String!
  author: User!
  tags: [Tag!]
  createdAt: String!
}

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

# 查询类型
type Query {
  users(limit: Int, offset: Int): [User!]!
  user(id: ID!): User
  posts(keyword: String, limit: Int): [Post!]!
  post(id: ID!): Post
}

# 变更类型
type Mutation {
  createUser(input: CreateUserInput!): User!
  updateUser(id: ID!, input: UpdateUserInput!): User!
  deleteUser(id: ID!): Boolean!
  
  createPost(input: CreatePostInput!): Post!
}

# 输入类型
input CreateUserInput {
  username: String!
  email: String!
  password: String!
  age: Int
}

input UpdateUserInput {
  username: String
  email: String
  age: Int
}

input CreatePostInput {
  title: String!
  content: String!
  tagIds: [ID!]
}

2.2 查询示例

# 查询用户列表
query GetUsers {
  users(limit: 10) {
    id
    username
    email
    posts {
      id
      title
    }
  }
}

# 查询单个用户
query GetUser($id: ID!) {
  user(id: $id) {
    id
    username
    email
    posts {
      id
      title
      createdAt
    }
  }
}

# 创建用户
mutation CreateUser($input: CreateUserInput!) {
  createUser(input: $input) {
    id
    username
    email
  }
}


三、Node.js实现(Apollo Server)

# 安装
npm install @apollo/server graphql

// server.js
import { ApolloServer } from '@apollo/server'
import { startStandaloneServer } from '@apollo/server/standalone'

// 模拟数据
let users = [
  { id: '1', username: '张三', email: 'zhangsan@example.com', age: 25 },
  { id: '2', username: '李四', email: 'lisi@example.com', age: 30 }
]

let posts = [
  { id: '1', title: '第一篇文章', content: '内容...', authorId: '1' }
]

// Schema定义
const typeDefs = `
  type User {
    id: ID!
    username: String!
    email: String!
    age: Int
    posts: [Post!]
  }
  
  type Post {
    id: ID!
    title: String!
    content: String!
    author: User!
  }
  
  type Query {
    users(limit: Int): [User!]!
    user(id: ID!): User
    posts: [Post!]!
  }
  
  type Mutation {
    createUser(username: String!, email: String!, age: Int): User!
    deleteUser(id: ID!): Boolean!
  }
`

// Resolver
const resolvers = {
  Query: {
    users: (_, { limit = 10 }) => users.slice(0, limit),
    user: (_, { id }) => users.find(u => u.id === id),
    posts: () => posts,
  },
  
  Mutation: {
    createUser: (_, { username, email, age }) => {
      const user = {
        id: String(users.length + 1),
        username,
        email,
        age
      }
      users.push(user)
      return user
    },
    
    deleteUser: (_, { id }) => {
      const index = users.findIndex(u => u.id === id)
      if (index === -1) return false
      users.splice(index, 1)
      return true
    }
  },
  
  // 关联解析
  User: {
    posts: (parent) => posts.filter(p => p.authorId === parent.id)
  },
  
  Post: {
    author: (parent) => users.find(u => u.id === parent.authorId)
  }
}

// 创建服务器
const server = new ApolloServer({ typeDefs, resolvers })

// 启动
const { url } = await startStandaloneServer(server, { listen: { port: 4000 } })
console.log(`🚀 Server ready at ${url}`)


四、Python实现(Strawberry)

# 安装
pip install strawberry-graphql

# schema.py
import strawberry
from typing import List, Optional

@strawberry.type
class User:
    id: int
    username: str
    email: str
    age: Optional[int] = None

@strawberry.type
class Post:
    id: int
    title: str
    content: str
    author_id: int

# 模拟数据
users = [
    User(id=1, username='张三', email='zhangsan@example.com', age=25),
    User(id=2, username='李四', email='lisi@example.com', age=30),
]

@strawberry.type
class Query:
    @strawberry.field
    def users(self, limit: int = 10) -> List[User]:
        return users[:limit]
    
    @strawberry.field
    def user(self, id: int) -> Optional[User]:
        return next((u for u in users if u.id == id), None)

@strawberry.type
class Mutation:
    @strawberry.mutation
    def create_user(self, username: str, email: str, age: int = None) -> User:
        user = User(id=len(users) + 1, username=username, email=email, age=age)
        users.append(user)
        return user

schema = strawberry.Schema(query=Query, mutation=Mutation)

# FastAPI集成
from fastapi import FastAPI
from strawberry.fastapi import GraphQLRouter

app = FastAPI()
graphql_app = GraphQLRouter(schema)
app.include_router(graphql_app, prefix="/graphql")


五、前端使用

5.1 Apollo Client(React)

npm install @apollo/client graphql

// Apollo Client配置
import { ApolloClient, InMemoryCache, ApolloProvider, useQuery, useMutation, gql } from '@apollo/client'

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

// 查询
const GET_USERS = gql`
  query GetUsers($limit: Int) {
    users(limit: $limit) {
      id
      username
      email
    }
  }
`

function UserList() {
  const { loading, error, data } = useQuery(GET_USERS, {
    variables: { limit: 10 }
  })
  
  if (loading) return <p>Loading...</p>
  if (error) return <p>Error: {error.message}</p>
  
  return (
    <ul>
      {data.users.map(user => (
        <li key={user.id}>{user.username} - {user.email}</li>
      ))}
    </ul>
  )
}

// 变更
const CREATE_USER = gql`
  mutation CreateUser($username: String!, $email: String!) {
    createUser(username: $username, email: $email) {
      id
      username
    }
  }
`

function CreateUser() {
  const [createUser] = useMutation(CREATE_USER)
  
  const handleSubmit = async (e) => {
    e.preventDefault()
    const formData = new FormData(e.target)
    await createUser({
      variables: {
        username: formData.get('username'),
        email: formData.get('email')
      }
    })
  }
  
  return (
    <form onSubmit={handleSubmit}>
      <input name="username" placeholder="用户名" />
      <input name="email" placeholder="邮箱" />
      <button type="submit">创建</button>
    </form>
  )
}


六、最佳实践

✅ 使用 DataLoader 避免 N+1 查询
✅ 定义清晰的 Schema
✅ 使用输入类型验证
✅ 实现分页
✅ 错误处理
✅ 认证和授权
✅ 查询复杂度限制
✅ 缓存策略


学习建议

  1. 先理解Schema和Resolver的概念
  2. 学会定义类型和查询
  3. 掌握前端Apollo Client
  4. 理解N+1问题和DataLoader
  5. 实践REST vs GraphQL对比

下一步学习

返回首页