GraphQL是一种现代的API查询语言,它在现代Web应用中得到了广泛的应用,因为它提供了一种高效、灵活且强大的方式来获取数据
GraphQL基础快速应用示例:
1. 后端设置(使用graphql-yoga)
首先,我们需要创建一个GraphQL服务器。安装graphql-yoga并创建一个简单的GraphQL schema:
npm init -y
npm install graphql yoga graphql-yoga
# server.js
const { GraphQLServer } = require('graphql-yoga');
const typeDefs = `
type Query {
hello: String
}
type Mutation {
addMessage(message: String!): String
}
`;
const resolvers = {
Query: {
hello: () => 'Hello world!',
},
Mutation: {
addMessage: (_, { message }) => `You added the message "${message}"`,
},
};
const server = new GraphQLServer({ typeDefs, resolvers });
server.start(() => console.log(`Server is running on http://localhost:4000`));
2. 前端设置(使用Apollo Client)
接着,我们需要在前端应用中配置Apollo Client,与我们的GraphQL服务器通信:
npm install apollo-boost @apollo/client graphql
# client.js
import ApolloClient from 'apollo-boost';
import { InMemoryCache } from '@apollo/client';
const client = new ApolloClient({
uri: 'http://localhost:4000/graphql',
cache: new InMemoryCache(),
});
export default client;
3. 编写前端组件
现在,我们在React组件中使用Apollo Client执行查询和变更:
// App.js
import React from 'react';
import { gql, useQuery, useMutation } from '@apollo/client';
import client from './client';
const GET_HELLO = gql`
query GetHello {
hello
}
`;
const ADD_MESSAGE_MUTATION = gql`
mutation AddMessage($message: String!) {
addMessage(message: $message)
}
`;
function App() {
const { loading, error, data } = useQuery(GET_HELLO);
const [addMessage, { data: mutationData }] = useMutation(ADD_MESSAGE_MUTATION);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error :(</p>;
return (
<div>
{data.hello}
<button onClick={() => addMessage({ variables: { message: 'Hello from frontend!' } })}>
Add Message
</button>
{mutationData && <p>New message: {mutationData.addMessage}</p>}
</div>
);
}
export default App;
我们创建了一个GET_HELLO查询来获取服务器的问候语,并在页面上显示。同时,我们定义了一个ADD_MESSAGE_MUTATION变更操作,当用户点击按钮时,它将向服务器发送一个新消息。
4. 运行应用
启动后端服务器:
node server.js
然后启动前端应用,假设使用Create React App:
npm start
GraphQL基本查询
1. 查询语言:查询、突变、订阅
在GraphQL中,查询和突变是通过JSON-like结构表示的字符串。这里有一个简单的示例:
# 查询示例
query GetUser {
user(id: 1) {
name
email
}
}
# 突变示例
mutation CreateUser {
createUser(name: "Alice", email: "alice@example.com") {
id
name
}
}
# 订阅示例(假设使用WebSocket)
subscription OnNewUser {
newUser {
id
name
}
}
在上述代码中,GetUser
查询请求了用户ID为1的用户姓名和电子邮件。CreateUser
突变创建了一个新用户并返回新用户的ID和姓名。OnNewUser
订阅等待新用户被创建时触发,返回新用户的信息。
2. 类型系统
在后端,我们定义GraphQL schema来描述这些类型:
type User {
id: ID!
name: String!
email: String!
}
type Mutation {
createUser(name: String!, email: String!): User
}
type Subscription {
newUser: User
}
这里定义了一个User对象类型,一个Mutation类型用于突变操作,和一个Subscription类型用于订阅操作。
3. 查询结构:字段和参数
查询结构由字段和参数组成。在上面的查询示例中,user是字段,id和email是user字段的子字段。参数如id: 1用于定制查询。
4. 层次结构和嵌套
GraphQL查询可以嵌套,以下是一个更复杂的例子:
query GetUsersAndPosts {
users {
id
name
posts {
id
title
content
author {
id
name
}
}
}
}
此查询请求所有用户及其各自的帖子,帖子还包含了作者的信息。层次结构允许一次请求获取多个级别的数据。
客户端代码示例(使用Apollo Client)
import { gql, useQuery } from '@apollo/client';
const GET_USERS_AND_POSTS = gql`
query GetUsersAndPosts {
users {
id
name
posts {
id
title
content
author {
id
name
}
}
}
}
`;
function App() {
const { loading, error, data } = useQuery(GET_USERS_AND_POSTS);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error :-(</p>;
return (
<div>
{data.users.map(user => (
<div key={user.id}>
<h2>{user.name}</h2>
<ul>
{user.posts.map(post => (
<li key={post.id}>
<h3>{post.title}</h3>
<p>{post.content}</p>
<p>Author: {post.author.name}</p>
</li>
))}
</ul>
</div>
))}
</div>
);
}
export default App;
在这个React组件中,我们使用useQuery从GraphQL服务器获取数据,并渲染用户和他们的帖子信息。这就是GraphQL查询、类型系统和层次结构在实际应用中的体现。
GraphQL Schema
GraphQL Schema Definition Language(SDL)是一种用于描述GraphQL schema的语言,它以简洁的人类可读格式定义了数据类型、查询、突变和指令等。
定义类型 首先,我们定义一些基本的数据类型。比如,定义一个User类型和一个Post类型。
type User {
id: ID!
username: String!
email: String!
posts: [Post!]!
}
type Post {
id: ID!
title: String!
content: String!
author: User!
}
这里,User类型有id、username、email字段,以及一个关联到多个Post的posts字段。而Post类型包含id、title、content字段,还有一个指向User的author字段。
查询根和突变根 接下来,定义GraphQL的查询根(Query)和突变根(Mutation)类型,它们是客户端请求数据和修改数据的入口点。
type Query {
user(id: ID!): User
allUsers: [User!]!
post(id: ID!): Post
allPosts: [Post!]!
}
type Mutation {
createUser(username: String!, email: String!): User
createPost(title: String!, content: String!, userId: ID!): Post
}
在Query类型中,我们定义了获取单个用户、所有用户、单篇帖子和所有帖子的查询。而在Mutation类型中,我们定义了创建新用户和新帖子的操作。
Directives的理解和使用 Directives是GraphQL schema中用于改变执行行为的指令。它们可以被应用到类型系统定义的任何部分,比如字段、输入类型、对象类型等。下面展示如何使用一个自定义的@auth指令来控制访问权限。
首先,假设我们定义了一个@auth指令,用于限制对某些字段的访问,要求用户必须登录。
scalar DateTime
directive @auth(requires: Role = ADMIN) on FIELD_DEFINITION
enum Role {
ADMIN
USER
}
接着,在schema中应用这个指令:
type Query {
me: User @auth(requires: USER)
}
type User {
id: ID!
username: String!
email: String! @auth(requires: ADMIN)
posts: [Post!]!
}
在上面的例子中,me查询和username字段无需特殊权限即可访问,但访问用户的email字段则需要管理员权限(通过@auth(requires: ADMIN)指令指定)。
GraphQL 高级应用
1. 分页
使用GraphQL Cursor-based分页,以提高性能和用户体验。
Schema定义:
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}
extend type Query {
users(first: Int, after: String, last: Int, before: String): [User!]!
usersConnection(first: Int, after: String, last: Int, before: String): UserConnection!
}
type UserConnection {
edges: [UserEdge!]!
pageInfo: PageInfo!
}
type UserEdge {
cursor: String!
node: User!
}
Resolver示例:
const resolvers = {
Query: {
users: (parent, args, context, info) => {
// 实现逻辑,根据args.first, args.after等参数进行分页查询
},
usersConnection: (parent, args, context, info) => {
// 实现逻辑,返回带有分页信息的UserConnection对象
},
},
};
2. 错误处理
自定义错误处理,提升客户端对错误的处理能力。
Resolver示例:
const resolvers = {
Mutation: {
createUser: async (parent, args, context, info) => {
try {
// 创建用户逻辑
} catch (error) {
throw new Error("Failed to create user", { extensions: { code: "USER_CREATION_FAILED" } });
}
},
},
};
3. 自定义指令
创建自定义指令以实现特定业务逻辑或安全需求。
Schema定义:
directive @log on FIELD_DEFINITION
Resolver示例:
javascript
const directiveResolvers = {
log: (next, source, args, context, info) => {
console.log(`Executing field: ${info.fieldName}`);
return next();
},
};
确保在GraphQL服务器配置中注册此指令处理器。
4. GraphQL Federation
Federation允许构建由多个服务组成的单一GraphQL API。
Service A Schema:
extend schema
@link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key", "@shareable"])
type Product @key(fields: "upc") {
upc: String! @external
price: Float
}
Service B Schema:
extend schema
@link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key"])
type Review {
body: String
author: User @provides(fields: "username")
}
extend type User @key(fields: "id") {
id: ID! @external
username: String
}
5. 复杂查询优化
利用GraphQL的字段解析器和数据加载器进行性能优化。
Data Loader示例:
const dataLoader = new DataLoader(keys => db.batchLoadUsers(keys));
const resolvers = {
User: {
friends: (parent, args, context, info) => {
return dataLoader.load(parent.id);
},
},
};
GraphQL 特点与优势
- 性能优化:通过按需获取数据,减少了网络传输开销,提高了页面加载速度。
- 减少错误:客户端定义查询结构,服务器返回预期的形状,降低了由于接口不匹配导致的错误。
- 更好的API设计:强类型系统确保了数据的一致性和正确性,使得API更加易于理解和维护。
- 客户端控制:客户端可以决定获取多少数据,何时获取,提高了用户体验。
- 缓存优化:客户端可以根据返回的数据结构更容易地进行缓存策略的实施。
- 减少后端复杂性:后端不再需要为了适应不同客户端的需求而创建多个API端点。