适用人群:后端开发者、前端开发者
学习时长:约1-2天
工具:Apollo Server / Express GraphQL
适用场景:复杂数据查询、移动端API
一、GraphQL是什么?
GraphQL是Facebook开发的API查询语言,客户端可以精确请求需要的数据。
| 对比项 | REST | GraphQL |
|---|---|---|
| 端点 | 多个端点 | 单个端点 |
| 数据 | 固定结构 | 按需获取 |
| 版本 | 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
✅ 使用输入类型验证
✅ 实现分页
✅ 错误处理
✅ 认证和授权
✅ 查询复杂度限制
✅ 缓存策略
学习建议
- 先理解Schema和Resolver的概念
- 学会定义类型和查询
- 掌握前端Apollo Client
- 理解N+1问题和DataLoader
- 实践REST vs GraphQL对比
下一步学习
- Apollo官方文档
- Hasura — 自动GraphQL API
- Prisma — 数据库ORM