更轻松的使用GraphQL

更轻松的使用GraphQL

引言

GraphQL是Facebook开发的一套数据查询解决方案,让我们先来看一下官方的定义:

GraphQL is a query language for your API, and a server-side runtime for executing queries by using a type system you define for your data. GraphQL isn't tied to any specific database or storage engine and is instead backed by your existing code and data.

翻译过来就是:

GraphQL是对你的API的一种查询语言,并且提供了对你采用类型系统所定义的数据进行查询的服务器端运行时方案。GraphQL并不与特定的数据库或存储引擎绑定,而是能对你现有的代码和数据进行支持。

其中有2个重点:

  1. 一种查询语言
  2. 服务器端运行时方案

在网上能找到的文章往往对第一点描述的比较详细,而且这一点也确实比较吸引人。但对于关键的第二点,如何实现这套查询机制的介绍却很难找到。

以一个简单的blog为例

假设我们的blog有以下两张表:

用户表中的数据:

uidnameavatar
1Tomhttps://pre00.deviantart.net/2930/th/pre/i/2014/182/a/2/tom_cat_by_1997ael-d7ougoa.png
2Jerryhttps://vignette.wikia.nocookie.net/tomandjerry/images/2/29/Jerry_2.png

帖子表中的数据(考虑到允许用户修改头像,所以帖子表中不冗余作者的信息,而只有作者的ID):

pidtitlecontentauthorId
1fooxxx1
2baryyy2

然后,界面大致是上下两栏模式的,上部是帖子标题、内容等;下部是作者的名字、头像等。让我们来看一下resuful和GraphQL方案的实现对比。

restful接口

如果采用restful方案,我们通常会设计如下两个接口:

  • 查询帖子内容:GET /posts/:id
  • 查询作者信息:GET /users/:id

然后,前端先调用拉取帖子内容的接口,拿到类似如下的返回结果:

GET /posts/1

{
    "code": 0,
    "reason": "success",
    "data": {
        "pid": 1,
        "title": "foo",
        "content": "xxx",
        "authorId": 1
    }
}

然后,再根据上述结果中的authorId去调用拉取用户信息的接口,来获取作者的相关信息:

GET /users/1

{
    "code": 0,
    "reason": "success",
    "data": {
        "uid": 1,
        "name": "Tom",
        "avatar": "https://pre00.deviantart.net/2930/th/pre/i/2014/182/a/2/tom_cat_by_1997ael-d7ougoa.png"
    }
}

在Web前端这样调用问题还不大,但遇到App时,由于绘制界面是一体化的,所以必须要两个restful接口都调用完毕,才能绘制界面。

而随着需求的变化,这个页面可能还会要展现评论、评论发表者的头像,等等等等;这就会导致这里需要调用的接口越来越多,从而使得App渲染这个界面的速度越来越慢。

GraphQL方式

采用GraphQL方式,我们首先需要对数据进行类型定义:

用户定义:

# user schema
type User {
    uid : ID!
    name : String!
    avatar : String!
}

帖子定义:

# post schema
type Post {
    pid : ID!
    title : String!
    content : String!
    author : User!
}

查询定义:

type Query {
    post(id: ID): Post
}

然后,我们根据界面要求编写查询语句,因为界面要求同时展现帖子内容和作者信息,所以会有如下的GraphQL查询语句:

query {  
  post(id:1) {
    pid
    title
    content
    author {
      uid
      name
      avatar
    }
  }
}

因为数据定义中,post下的author成员是User类型的,所以我们只需要通过一次查询就能够拿到绘制界面所需的数据:

{
  "data": {
    "post": {
      "pid": "1",
      "title": "foo",
      "content": "xxx",
      "author": {
        "uid": "1",
        "name": "Tom",
        "avatar": "https://pre00.deviantart.net/2930/th/pre/i/2014/182/a/2/tom_cat_by_1997ael-d7ougoa.png"
      }
    }
  }
}

看到这里大家一定能体会到GraphQL的查询语言的爽点所在了,但网上大多数的资料也往往是继续介绍这个查询语言的更多语法,但对于服务器端如何执行查询却介绍的不够深入,所给出的简单的例子甚至是上述数据结构中的每一个成员变量都要写一个对应的resolver函数来进行查询的情况。

GraphQL 的服务器端解决方案

由于存在如上痛点,笔者在进行了相关的探索后,封装了一个使用上更简便的npm库(easy-graphql)。

easy-graphql设计了一套约定,使得开发会更便捷和规范: SQR

  • S - Schemas,即数据的类型定义
  • Q - Query,即对外提供的查询接口
  • R - Resolvers,即如何查询数据的函数实现
使用步骤
1. 根据SQR约定创建目录

按照上述约定来建立目录结构,指定的目录下存放对应的文件,比如上文blog的例子,我们建立的目录格式如下:

  1. 建立graphql目录作为根目录
  2. graphql下建立schemasresolvers两个子目录,分别用于存放数据类型定义文件和对应的查询解决实现函数文件
  3. 建立query.graphqls文件,用于对外提供的查询接口定义
graphql             # GraphQL相关定义、代码的跟目录
├── query.graphqls  # 对外提供的查询接口定义文件
├── resolvers       # 如何查询数据的函数实现文件所在目录
│   ├── post_resolver.js
│   └── user_resolver.js
└── schemas         # 数据的类型定义文件所在目录
    ├── post_schema.graphqls
    └── user_schema.graphqls
2. 创建数据类型定义(schema)文件

文件存放在graphql/schemas目录下,命名规则:xxx_schema.graphqls

帖子和用户的数据类型定义上文已有,此处不再赘述

3. 创建查询接口定义(query)文件

文件放在graphql目录下,命名为:query.graphqls

4. 创建数据查询的函数实现(reslver)文件

文件放在graphql/reslvers目录下,命名规则:xxx_resolver.js

这里就只针对上文提及的帖子内容和作者信息的查询是如何实现的(resolvers/post_reslver.js):

'use strict'

const fakeDB = require('../../fakeDB');

function fetchPostById (root, {id}, ctx) {
    // post的查询,第二个参数是查询语句中传入的
    let pid = parseInt(id);
    return fakeDB.getPostById(pid);
}

// 对post下的author字段进行查询解决的函数
function fetchUserByAuthorId (root, args, ctx) {
    // 执行完post的数据查询后,遇到需要author字段的情况,会再来调用本函数,root参数就是前一步查询完的post数据
    let uid = root.authorId;
    return fakeDB.getUserById(uid);
}

const postReolvers = {
    Query : {
        post : fetchPostById,
    },

    Post : {
        // 针对嵌套的数据结构需要的解决函数
        author : fetchUserByAuthorId,
    },
};
module.exports = postReolvers;
5. 初始化

新建一个easy-graphql对象:

const path = require('path');

const easyGraphqlModule = require('easy-graphql');

const basePath = path.join(__dirname, 'graphql');
const easyGraphqlObj = new easyGraphqlModule(basePath);
  • 可视化IDE调试

对于采用node.js来进行开发的话,GraphQL提供了可视化的图形化的Web界面来编写、调试查询语句。

express插件:express-graphql KOA插件:koa-graphql

easy-graphql配合express-graphql使用:

const express = require('express');
const graphqlHTTP = require('express-graphql');

const allSchema = easyGraphqlObj.getSchema();

// using with express-graphql middleware
app.use('/graphql', graphqlHTTP({
    schema : allSchema,
    graphiql : true,
}));

然后,就能在浏览器中,直接访问对应的网址,打开可视化IDE调试界面了,效果如下图所示:

  • 直接接口形式

使用上面的中间件方案,我们已经可以实现对外提供GraphQL查询的能力了,但往往我们的项目已经有约定好的返回数据结构了,比如:

{
    "code" : 0,
    "reason" : "success",
    "data" : {...}
}

而直接采用插件形式,并不能自定义返回的数据结构,所以easy-graphql又提供了一个直接执行GraphQL查询语句的API:

/**
 * do the GraphQL query execute
 * @param {*} requestObj -  GraphQL query object {query: "..."}
 * @param {*} context - [optional] query context
 * @returns {Promise} - GraphQL execute promise 
 */
queryGraphQLAsync(requestObj, {context})

以使用express框架为例,我们可以自己实现一个接口供前端调用:

const bodyParser = require('body-parser');
app.use(bodyParser.json()); // for parsing application/json
app.use(bodyParser.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded

app.post('/restful', async (req, res) => {
    let queryObj = req.body;
    
    let result;
    try {
        // using with your restful service
        result = await easyGraphqlObj.queryGraphQL(queryObj, {context: req});
    } catch (err) {
        console.error(err);
        res.json({code : -1, reason : "GraphQL error"});
        return;
    }
    
    res.json({
        code : 0,
        reason : "success",
        data : result.data,
    });
});
完整示例

完整的代码示例,请前往gayhub上的test目录下查看,欢迎大家给这个项目点赞!

参考资料

转载于:https://my.oschina.net/u/3662177/blog/1559479

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: GraphQL是一种用于客户端和服务器通信的技术,它可以帮助开发人员轻松地构建和查询数据。下面是使用GraphQL前端中的一个示例代码: const query = ` query { allBooks { title author } } `; fetch('/graphql', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, body: JSON.stringify({query}) }) .then(r => r.json()) .then(data => console.log('data returned:', data)); ### 回答2: GraphQL是一种用于前端开发的查询语言和运行时,在前端使用GraphQL需要通过客户端库与后端服务器进行通信。下面是一个简单的代码示例,展示了如何在前端使用GraphQL。 首先,需要在项目中安装所需的依赖库,如`graphql`和`apollo-client`。可以使用npm或yarn来安装: ```bash npm install graphql apollo-client ``` 接下来,需要创建一个Apollo客户端来处理与GraphQL服务器的通信。可以在项目的入口文件或任何需要使用GraphQL的组件中创建: ```javascript import { ApolloClient, InMemoryCache } from 'apollo-client'; import { createHttpLink } from 'apollo-link-http'; import { ApolloProvider } from '@apollo/react-hooks'; const httpLink = createHttpLink({ uri: 'http://example.com/graphql', // 替换为实际的GraphQL服务器URL }); const client = new ApolloClient({ link: httpLink, cache: new InMemoryCache(), }); // 将客户端提供给整个应用程序 ReactDOM.render( <ApolloProvider client={client}> <App /> </ApolloProvider>, document.getElementById('root') ); ``` 现在,可以在前端组件中使用`useQuery`和`useMutation`钩子函数来发起GraphQL查询和变操作。这些钩子函数是来自`@apollo/react-hooks`包的一部分。 ```javascript import React from 'react'; import { useQuery, useMutation } from '@apollo/react-hooks'; import { gql } from 'apollo-boost'; // 定义GraphQL查询和变操作 const GET_USERS = gql` query GetUsers { users { id name email } } `; const CREATE_USER = gql` mutation CreateUser($input: UserInput!) { createUser(input: $input) { id name email } } `; function UserList() { const { loading, error, data } = useQuery(GET_USERS); if (loading) return <p>Loading...</p>; if (error) return <p>Error :(</p>; return ( <ul> {data.users.map(user => ( <li key={user.id}>{user.name} - {user.email}</li> ))} </ul> ); } function UserForm() { const [createUser] = useMutation(CREATE_USER); const handleSubmit = async (event) => { event.preventDefault(); const form = event.target; const input = { name: form.name.value, email: form.email.value, }; await createUser({ variables: { input } }); form.reset(); }; return ( <form onSubmit={handleSubmit}> <input type="text" name="name" placeholder="Name" required /> <input type="email" name="email" placeholder="Email" required /> <button type="submit">Create User</button> </form> ); } function App() { return ( <div> <h1>User List:</h1> <UserList /> <h1>Create User:</h1> <UserForm /> </div> ); } export default App; ``` 以上代码示例演示了在前端使用GraphQL的基本流程。使用`useQuery`钩子函数发起查询操作,并在返回的数据中处理加载状态和错误。使用`useMutation`钩子函数发起变操作,并使用`variables`选项传递变量数据。 ### 回答3: GraphQL 是一种用于前端开发的查询语言和运行时环境。以下是一个代码示例,展示了如何在前端使用 GraphQL: 1. 安装所需的库和依赖项: 首先,在你的项目目录下,通过 npm 或 yarn 安装以下库: - graphql: 提供了 GraphQL 的核心功能; - apollo-boost: 提供了一些 Apollo 客户端需要的必要设置。 ```bash npm install graphql apollo-boost ``` 2. 创建一个 Apollo 客户端: 在你的代码中,导入 ApolloClient、HttpLink 和 InMemoryCache 并创建一个 Apollo Client 实例。 ```javascript import { ApolloClient, HttpLink, InMemoryCache } from 'apollo-boost'; const httpLink = new HttpLink({ uri: 'http://example.com/graphql', // 替换为你的 GraphQL API 地址 }); const client = new ApolloClient({ link: httpLink, cache: new InMemoryCache(), }); ``` 3. 编写 GraphQL 查询: 使用 GraphQL 查询语法编写你需要的查询。 ```javascript import { gql } from 'apollo-boost'; const GET_USERS = gql` query { users { id name email } } `; ``` 4. 发起查询: 使用 Apollo Client 的 `query` 方法来发起查询,然后处理返回的数据。 ```javascript client.query({ query: GET_USERS }).then((response) => { console.log(response.data.users); }); ``` 5. 在组件中使用查询结果: 将查询结果传递给你的组件,并在 render 方法中使用它。 ```javascript import React from 'react'; class UserList extends React.Component { state = { users: [], }; componentDidMount() { this.fetchUsers(); } fetchUsers() { client.query({ query: GET_USERS }).then((response) => { this.setState({ users: response.data.users }); }); } render() { return ( <div> <h1>User List</h1> <ul> {this.state.users.map((user) => ( <li key={user.id}> {user.name} - {user.email} </li> ))} </ul> </div> ); } } export default UserList; ``` 这只是一个简单的示例,演示了如何在前端使用 GraphQLGraphQL 的优点之一是可以根据特定需求编写精确的查询,从而减少数据传输和解析的开销。在实际开发中,你还可以使用复杂的查询、变量和多功能来满足你的需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值