GraphQL 在现代 Web 应用程序中的应用和优势
GraphQL 是一种现代 API 查询语言,它提供了一种高效、灵活且强大的方法来获取数据,因此被广泛应用于现代 Web 应用程序中
GraphQL 基础快速应用示例:
1.后端设置(使用graphql-yoga)
首先,我们需要创建一个 GraphQL 服务器。安装 graphql-yoga 并创建一个简单的 GraphQL 模式:
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 Loading...
;
  if (error) return Error :(
;
  return (
    
      {data.hello}
      
      {mutationData && New message: {mutationData.addMessage}
}
    
  );
}
export default App;我们创建一个 GET_HELLO 查询来获取服务器的问候语并将其显示在页面上。同时,我们定义一个 ADD_MESSAGE_MUTATION 变异操作,当用户点击按钮时,它将向服务器发送一条新消息。
4. 运行应用程序
启动后端服务器:
node server.js
然后启动前端应用程序,假设创建 React App:
npm start
GraphQL 基本查询
1. 查询语言:查询、变更、订阅
在 GraphQL 中,查询和修改都是用类似 JSON 的结构表示的字符串。这是一个简单的例子:
# Query Example
query GetUser {
  user(id: 1) {
    name
    email
  }
}
# Mutation Example
mutation CreateUser {
  createUser(name: "Alice", email: "alice@example.com") {
    id
    name
  }
}
# Subscription Example (Assuming WebSocket)
subscription OnNewUser {
  newUser {
    id
    name
  }
}在上面的代码中,`GetUser` 查询请求用户 ID 为 1 的用户的姓名和电子邮件。`CreateUser` 突变创建一个新用户并返回新用户的 ID 和姓名。`OnNewUser` 订阅等待新用户创建并返回新用户的信息。
2. 类型系统
在后端,我们定义一个 GraphQL 模式来描述这些类型:
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 客户端)
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 Loading...
;
  if (error) return Error :-(
;
  return (
    
      {data.users.map(user => (
        
          {user.name}
          
            {user.posts.map(post => (
              - 
                
{post.title}
                {post.content}
                Author: {post.author.name}
               
            ))}
          
        
      ))}
    
  );
}
export default App;在此 React 组件中,我们使用 useQuery 从 GraphQL 服务器获取数据并呈现有关用户及其帖子的信息。这就是 GraphQL 查询、类型系统和层次结构发挥作用的方式。
GraphQL 模式
GraphQL 模式定义语言 (SDL) 是一种用于描述 GraphQL 模式的语言。它以简洁、人性化且易于理解的格式定义数据类型、查询、变更和指令。
**定义类型**
首先我们来定义一些基本的数据类型,比如定义一个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 字段,以及一个链接到多个 Posts 的 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 类型中,我们定义创建新用户和新帖子的操作。
**理解和使用指令**
指令是 GraphQL 架构中用于更改执行行为的指令。它们可以应用于类型系统定义的任何部分,例如字段、输入类型、对象类型等。下面展示了如何使用自定义 @auth 指令来控制访问权限。
首先,假设我们定义一个@auth指令来限制对某些字段的访问并要求用户登录。
scalar DateTime
directive @auth(requires: Role = ADMIN) on FIELD_DEFINITION
enum Role {
  ADMIN
  USER
}接下来,在模式中应用此指令:
type Query {
  me: User @auth(requires: USER)
}
type User {
  id: ID!
  username: String!
  email: String! @auth(requires: ADMIN)
  posts: [Post!]!
}在上面的例子中,me 查询和 username 字段无需特殊权限即可访问,但访问用户的电子邮件字段需要管理员权限(由 @auth(requires: ADMIN) 指令指定)。
GraphQL 高级应用程序
1. 分页
使用基于 GraphQL Cursor 的分页来提高性能和用户体验。
**架构定义:**
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!
}解析器示例:
const resolvers = {
  Query: {
    users: (parent, args, context, info) => {
      // Implement logic and perform paging queries based on parameters such as args.first, args.after, etc.
    },
    usersConnection: (parent, args, context, info) => {
      // Implement logic and return UserConnection object with paging information
    },
  },
};2. 错误处理
自定义错误处理,提高客户端处理错误的能力。
**解析器示例:**
const resolvers = {
  Mutation: {
    createUser: async (parent, args, context, info) => {
      try {
        // Creating User Logic
      } catch (error) {
        throw new Error("Failed to create user", { extensions: { code: "USER_CREATION_FAILED" } });
      }
    },
  },
};3. 自定义指令
创建自定义指令来实现特定的业务逻辑或安全要求。
**架构定义:**
directive @log on FIELD_DEFINITION
解析器示例:
const directiveResolvers = {
  log: (next, source, args, context, info) => {
    console.log(`Executing field: ${info.fieldName}`);
    return next();
  },
};确保在您的 GraphQL 服务器配置中注册此指令处理程序。
4. GraphQL 联邦
联合允许构建由多个服务组成的单个 GraphQL API。
**服务 A 模式:**
extend schema
  @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key", "@shareable"])
type Product @key(fields: "upc") {
  upc: String! @external
  price: Float
}**服务 B 架构:**
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 的字段解析器和数据加载器来优化性能。
**数据加载器示例:**
const dataLoader = new DataLoader(keys => db.batchLoadUsers(keys));
const resolvers = {
  User: {
    friends: (parent, args, context, info) => {
      return dataLoader.load(parent.id);
    },
  },
};