50、深入理解RESTful与GraphQL:从数据获取到灵活查询

深入理解RESTful与GraphQL:从数据获取到灵活查询

1. 消费RESTful Web服务

在开发中,消费RESTful Web服务是常见的数据获取方式。为了从Web服务获取初始产品和供应商数据,并确保任何更改都能触发对Web服务的更新,我们需要对初始数据进行处理。以下是移除 src/store 文件夹中 initialData.js 文件里静态数据的代码:

import { PRODUCTS, SUPPLIERS } from "./dataTypes";
export const initialData = {
    modelData: {
        [PRODUCTS]: [],
        [SUPPLIERS]: []
    },
    stateData: {
        editing: false,
        selectedId: -1,
        selectedType: PRODUCTS
    }
}

通过上述代码,我们将初始的产品和供应商数据设置为空数组,后续数据将从Web服务获取。

2. 理解GraphQL

GraphQL是一种用于创建和消费API的端到端系统,它为传统的RESTful Web服务提供了更灵活的替代方案。下面是关于GraphQL的一些关键信息:
| 问题 | 答案 |
| ---- | ---- |
| 是什么? | GraphQL是一种生成API的查询语言。 |
| 为什么有用? | 它为客户端提供了灵活的数据访问方式,确保客户端仅接收所需的数据,并允许在不进行服务器端更改的情况下制定新的查询。 |
| 如何使用? | 在服务器端,使用解析器函数定义并实现模式;客户端使用GraphQL语言发送查询和请求更改。 |
| 有哪些陷阱或限制? | GraphQL复杂,编写有用的模式需要一定的技能。 |
| 有哪些替代方案? | 客户端可以使用RESTful Web服务。 |

3. 准备工作

在开始使用GraphQL之前,我们需要进行一些准备工作。首先,打开命令提示符,导航到 productapp 文件夹,并运行以下命令添加所需的包:

npm install --save-dev graphql@14.0.2
npm install --save-dev express@4.16.4
npm install --save-dev express-graphql@0.7.1
npm install --save-dev graphql-import@0.7.1
npm install --save-dev cors@2.8.5

这些包的作用如下:
| 名称 | 描述 |
| ---- | ---- |
| graphql | 包含GraphQL的参考实现。 |
| express | 提供可扩展的HTTP服务器,是本章使用的GraphQL服务器的基础。 |
| express-graphql | 通过 express 包提供基于HTTP的GraphQL服务。 |
| graphql-import | 允许在多个文件中定义GraphQL模式,并比直接读取文件更轻松地导入模式。 |
| cors | 为Express HTTP服务器启用跨域资源共享(CORS)。 |

安装完包后,在 productapp 文件夹中运行以下命令启动开发工具:

npm start

启动完成后,会打开一个新的浏览器窗口并显示 http://localhost:3000

4. 创建GraphQL服务器

创建GraphQL服务器的过程包括创建模式、编写解析器和启动服务器。以下是详细步骤:
- 创建模式 :在 src/graphql 文件夹中创建一个名为 schema.graphql 的文件,内容如下:

type product {
    id: ID!,
    name: String!,
    category: String!
    price: Float!
}
type supplier {
    id: ID!,
    name: String!,
    city: String!,
    products: [ID]
}
type Query {
    products: [product],
    suppliers: [supplier]
}

这个模式定义了两个自定义类型: product supplier ,并定义了两个查询: products suppliers
- 创建解析器 :在 src/graphql 文件夹中创建一个名为 resolvers.js 的文件,代码如下:

var data = require("../../restData")();
module.exports = {
    products: () => data.products,
    suppliers: () => data.suppliers
}

每个解析器是一个函数,其名称对应于模式中定义的查询,并返回符合模式声明格式的数据。
- 创建服务器 :在 productapp 文件夹中创建一个名为 graphqlServer.js 的文件,代码如下:

var { buildSchema } = require("graphql");
var { importSchema } = require("graphql-import");
var express = require("express");
var graphqlHTTP = require("express-graphql")
var cors = require("cors")
var schema = importSchema("./src/graphql/schema.graphql");
var resolvers = require("./src/graphql/resolvers");
var app = express();
app.use(cors());
app.use("/graphql", graphqlHTTP({
  schema: buildSchema(schema),
  rootValue: resolvers,
  graphiql: true,
}));
app.listen(3600, () => console.log("GraphQL Server Running on Port 3600"));

启动GraphQL服务器,打开新的命令提示符,导航到 productapp 文件夹,运行以下命令:

node graphqlServer.js

启动后,打开浏览器并导航到 http://localhost:3600/graphql ,可以看到GraphiQL工具。

5. 执行GraphQL查询

使用GraphiQL工具可以轻松执行查询。例如,要查询所有供应商对象,在GraphiQL窗口的左窗格中输入以下查询:

query {
  suppliers { 
    id,
    name,
    city,
    products
  }
}

点击“Execute Query”按钮发送请求,服务器将返回以下结果:

{
  "data": {
    "suppliers": [
      {
        "id": "1",
        "name": "Surf Dudes",
        "city": "San Jose",
        "products": ["1","2"]
      },
      {
        "id": "2",
        "name": "Goal Oriented",
        "city": "Seattle",
        "products": ["3","4","5"]
      },
      {
        "id": "3",
        "name": "Bored Games",
        "city": "New York",
        "products": ["6","7","8","9"]
      }
    ]
  }
}

通过这个基本查询,客户端能够选择所需的字段并指定其显示顺序。

6. 查询相关数据

GraphQL的一个强大特性是它能轻松支持查询中的相关数据,允许单个查询返回包含多种类型的结果。我们对 schema.graphql 文件中的 supplier 数据类型的 products 字段进行修改:

type product {
    id: ID!,
    name: String!,
    category: String!
    price: Float!
}
type supplier {
    id: ID!,
    name: String!,
    city: String!,
    products: [product]
}
type Query {
    products: [product],
    suppliers: [supplier]
}

为了支持这个更改,我们需要处理解析器中使用的数据,修改 resolvers.js 文件如下:

var data = require("../../restData")();
module.exports = {
    products: () => data.products,
    suppliers: () => data.suppliers.map(s => ({
        ...s, products: () => s.products.map(id =>
            data.products.find(p => p.id === Number(id)))
    }))
}

停止GraphQL服务器(使用 Control+C ),然后重新启动:

node graphqlServer.js

导航到 http://localhost:3600/graphql ,在GraphiQL窗口的左窗格中输入以下查询:

query {
  suppliers {
    id,
    name,
    city,
    products {
      name
    }
  }
}

通过上述查询,我们可以在单个查询中获取供应商及其相关产品的数据。

7. 获取GraphQL服务的模式详细信息

如果无法编写自己的模式,可以通过以下方法获取GraphQL服务的模式详细信息:
- 查看开发者文档 :许多公共GraphQL服务会发布全面的模式文档,如GitHub API(https://developer.github.com/v4 )。
- 使用GraphiQL或类似工具 :大多数工具支持模式导航,例如GraphiQL可以通过其“Docs”链接轻松探索模式。
- 使用GraphQL内省功能 :如果没有文档且不支持GraphiQL,可以使用内省功能发送关于模式的查询,例如:

{
    __schema {
        queryType {
            fields {
                name
            }
        }
    }
}

综上所述,GraphQL为我们提供了一种更灵活的数据查询和获取方式,通过合理使用模式、解析器和查询,我们可以高效地满足不同的业务需求。但在使用时,也需要注意其复杂性和可能带来的成本问题。

深入理解RESTful与GraphQL:从数据获取到灵活查询

8. GraphQL查询过滤

在GraphQL中,我们可以通过指定查询参数来过滤结果。以下是一个示例,展示如何在查询中添加过滤条件。假设我们要查询价格大于某个值的产品,我们可以修改 schema.graphql 文件,添加一个带参数的查询:

type product {
    id: ID!,
    name: String!,
    category: String!
    price: Float!
}
type Query {
    products(priceGreaterThan: Float): [product]
}

然后修改 resolvers.js 文件来处理这个参数:

var data = require("../../restData")();
module.exports = {
    products: (args) => {
        if (args.priceGreaterThan) {
            return data.products.filter(p => p.price > args.priceGreaterThan);
        }
        return data.products;
    }
}

在GraphiQL中,我们可以这样使用这个带参数的查询:

query {
    products(priceGreaterThan: 50) {
        id
        name
        price
    }
}

这样就可以只获取价格大于50的产品数据。

9. 使用GraphQL进行数据修改

GraphQL不仅可以用于查询数据,还可以用于修改数据。我们可以在 schema.graphql 中定义突变(mutations)来实现数据的修改。以下是一个简单的示例,定义一个用于更新产品价格的突变:

type product {
    id: ID!,
    name: String!,
    category: String!
    price: Float!
}
type Mutation {
    updateProductPrice(id: ID!, newPrice: Float!): product
}
type Query {
    products: [product],
    suppliers: [supplier]
}

然后在 resolvers.js 中实现这个突变:

var data = require("../../restData")();
module.exports = {
    products: () => data.products,
    suppliers: () => data.suppliers,
    updateProductPrice: (args) => {
        const product = data.products.find(p => p.id === args.id);
        if (product) {
            product.price = args.newPrice;
            return product;
        }
        return null;
    }
}

在GraphiQL中,我们可以这样使用这个突变:

mutation {
    updateProductPrice(id: "1", newPrice: 60) {
        id
        name
        price
    }
}

执行这个突变后,ID为1的产品价格将被更新为60。

10. 参数化查询

为了提高查询的灵活性,我们可以使用查询变量。以下是一个使用查询变量的示例:

query GetProducts($priceGreaterThan: Float) {
    products(priceGreaterThan: $priceGreaterThan) {
        id
        name
        price
    }
}

在GraphiQL的变量输入框中,我们可以这样设置变量:

{
    "priceGreaterThan": 50
}

这样就可以动态地改变查询的过滤条件。

11. 使用查询片段

当我们需要在多个查询中请求相同的字段集时,可以使用查询片段。以下是一个示例:

fragment ProductInfo on product {
    id
    name
    price
}
query {
    products {
       ...ProductInfo
    }
    suppliers {
        id
        name
        products {
           ...ProductInfo
        }
    }
}

通过使用查询片段,我们可以避免重复编写相同的字段,提高代码的可维护性。

12. GraphQL与RESTful的对比总结
对比项 RESTful GraphQL
数据获取灵活性 客户端可能需要多次请求并合并数据,获取的数据可能包含不需要的字段 客户端可以精确控制请求的数据字段,一次请求获取所需的所有数据
开发难度 易于上手,但随着客户端需求变化可能变得不灵活 相对复杂,编写有用的模式需要一定技能,但能更好地适应客户端需求变化
服务器压力 客户端可能会请求大量不必要的数据,增加服务器压力 客户端只请求所需的数据,减少服务器压力
13. 选择合适的技术

在选择使用RESTful还是GraphQL时,需要考虑以下因素:
- 项目规模和复杂度 :对于小型项目或需求相对固定的项目,RESTful可能是一个简单直接的选择;对于大型项目或需求变化频繁的项目,GraphQL可能更合适。
- 开发团队技能 :如果团队成员对RESTful比较熟悉,且项目需求不需要高度的灵活性,那么可以继续使用RESTful;如果团队有能力学习和掌握GraphQL,且项目需要更好地适应变化,那么可以考虑使用GraphQL。

graph LR
    A[选择技术] --> B{项目规模和复杂度}
    A --> C{开发团队技能}
    B --> D[小型/需求固定: RESTful]
    B --> E[大型/需求多变: GraphQL]
    C --> F[熟悉RESTful且需求简单: RESTful]
    C --> G[有能力掌握GraphQL且需求多变: GraphQL]

总之,GraphQL和RESTful各有优缺点,我们需要根据项目的具体情况来选择合适的技术,以实现高效的数据处理和业务需求的满足。在实际开发中,可以结合两者的优势,构建出更强大的应用程序。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值