In this article, we’ll be adding relations between object types and looking at some basic schema definition language syntax.
在本文中,我们将添加对象类型之间的关系,并研究一些基本的模式定义语言语法。
初始点
(
Starting Point
)
We’re building on the
basic GraphQL server that was set up in the previous article in this series
. If you just want the code for this
download it from here
or:
我们建立
在本系列上一篇文章中设置
的
基本GraphQL服务器上
。 如果您只想要此代码,请
从此处下载
或:
git clone --branch setup https://github.com/bjdixon/graphql-server.git
If you’re just cloning or downloading at this point you’ll need to create a MongoDB instance and replace the connection string in the
./index.js
file.
如果此时您只是克隆或下载,则需要创建一个MongoDB实例并替换
./index.js
文件中的连接字符串。
Make sure the dependencies are installed:
确保已安装依赖项:
npm install
Then start the server:
然后启动服务器:
npm start
We’re ready to dive in!
我们准备潜水了!
我们正在建设什么
(
What We’re Building
)
Right now we have two models that we can interact with — we’re able to create and list authors and books. Those models have a natural relationship that we would like to define and we should also be able to view individual authors and books, along with their related objects.
现在,我们有两个模型可以与之交互-我们能够创建并列出作者和书籍。 这些模型具有我们要定义的自然关系,我们还应该能够查看各个作者和书籍及其相关对象。
扩展架构
(
Extending the Schema
)
So far in our
./schema/index.js
file we have defined two object types,
Author
and
Book
using the Schema Definition Language (SDL).
到目前为止,在我们的
./schema/index.js
文件中,我们已经使用模式定义语言(SDL)定义了两种对象类型,即
Author
和
Book
。
type Author {
id: ID!
name: String!
}
type Book {
id: ID!
name: String!
pages: Int
}
Each object type has a list of properties and an associated type for each property. In our example we have a
Book
type that has three properties:
每个对象类型都有一个属性列表以及每个属性的关联类型。 在我们的示例中,我们的
Book
类型具有三个属性:
-
id
which is a special ID type.
id
是一种特殊的ID类型。
-
name
which is a string type.
name
是字符串类型。
-
pages
which is an int type.
pages
是int类型。
A list of all supported types
can be found here
.
可以在此处找到
所有受支持类型的列表。
You may notice the
!
at the end of some of the type definitions. This indicates that the property is required. A good illustration of this can be seen in our
Query
type:
您可能会注意到
!
在某些类型定义的末尾。 这表明该属性是必需的。 我们的
Query
类型可以很好地说明这一点:
type Query {
message: String!
authors: [Author!]!
books: [Book!]!
}
The
message
property requires a string, the
authors
property requires a list (lists are indicated by using square brackets) and the values in that list are required to be
Author
types, although returning an empty list would also be valid. For now, we want the
authors
property to be either null or a list and to make sure that if it does return a non-empty list then it only contains
Author
type data. Let’s also get rid of the static message query, we won’t be using it again:
message
属性需要一个字符串,
authors
属性需要一个列表(列表使用方括号表示),并且该列表中的值必须是
Author
类型,尽管返回一个空列表也是有效的。 现在,我们希望
authors
属性为null或列表,并确保如果它确实返回非空列表,则仅包含
Author
类型数据。 让我们也摆脱静态消息查询,我们将不再使用它:
type Query {
authors: [Author!]
books: [Book!]!
}
You can also delete the message resolver. Just delete the following line:
您也可以删除邮件解析器。 只需删除以下行:
message: () => 'hello world',
Apart from the message deletion, the difference to our queries is subtle. The author’s line no longer terminates with an exclamation mark. In the example above the
authors
property can return null or a list, but if it returns a non-empty list, that list can only contain authors. The
books
property must return a list (which can be empty) and that list may only contain books.
除了删除邮件外,我们查询的区别也是微妙的。 作者的行不再以感叹号结尾。 在上面的示例中,
authors
属性可以返回null或列表,但是如果它返回非空列表,则该列表只能包含authors。
books
属性必须返回一个列表(可以为空),并且该列表只能包含书籍。
With this knowledge we’ll create a couple of relations in our schema. To our
Book
type we will add a required author property (we’ll assume only one person can be an author of a book) and that value must be an
Author
type. To our
Author
type we will add a
books
property that can be null or a list and if that list is not empty the values must be
Book
types. Edit the following in
./schema/index.js
:
有了这些知识,我们将在架构中创建几个关系。 在我们的
Book
类型中,我们将添加一个必填的author属性(假设只有一个人可以是一本书的作者),并且该值必须是
Author
类型。 在我们的
Author
类型中,我们将添加一个
books
属性,该属性可以为null或一个列表,如果该列表不为空,则值必须为
Book
类型。 在
./schema/index.js
编辑以下
./schema/index.js
:
type Author {
id: ID!
name: String!
books: [Book!]
}
type Book {
id: ID!
name: String!
pages: Int
author: Author!
}
We can still list authors and return their names, but we’ll only be able to get null for their books. If we try to list books and return the authors of those books, we’ll get an error — books are now required to have an author of type
Author
and our book instances don’t have any authors yet.
我们仍然可以列出作者并返回他们的名字,但是我们只能使他们的书为空。 如果我们尝试列出书籍并返回这些书籍的作者,则会出现错误-现在要求书籍的作者类型为
Author
而我们的图书实例还没有任何作者。
We still have to update the underlying models and the mutations that create our books and authors.
我们仍然必须更新基础模型以及创建我们的书籍和作者的变异。
扩展模型
(
Extend the Models
)
We’ll extend the
Author
model first, by adding the books array. We’re actually just going to store a list of Mongo
ObjectId
s (as you’ll be able to see in the
type
property for books) and use the ref property to let Mongo know which model we want this to be related to. Edit
./models/Author.js
:
首先,通过添加books数组来扩展
Author
模型。 实际上,我们只是要存储Mongo
ObjectId
的列表(您将能够在book的
type
属性中看到),并使用ref属性让Mongo知道我们希望与之关联的模型。 编辑
./models/Author.js
:
import mongoose from 'mongoose'const Schema = mongoose.Schemaexport const Author = mongoose.model('Author', {
name: String,
books: [{
type: Schema.Types.ObjectId,
ref: 'Book'
}]
})
Similarly, we need to update the
Book
model but instead of an array of authors we’re adding a relation to a single Author. Edit this file
./models/Book.js
:
同样,我们需要更新
Book
模型,但要添加一个与单个Author的关系而不是Authors数组。 编辑此文件
./models/Book.js
:
import mongoose from 'mongoose'const Schema = mongoose.Schemaexport const Book = mongoose.model('Book', {
name: String,
pages: Number,
author: {
type: Schema.Types.ObjectId,
ref: 'Author'
}
})
Now we can change three lines in our schema and we’ll be able to create our first relations.
现在,我们可以更改架构中的三行,并且可以创建第一个关系。
First, in our
Mutation
type, we add an author when creating a new book. This will be a string, as Mongo is storing the ID of the author and will accept this as a string. In
./schema/index.js
edit
createBook
in the
Mutation
type adding the new
Author
parameter:
首先,在我们的
Mutation
类型中,我们在创建新书时会添加一个作者。 这将是一个字符串,因为Mongo将存储作者的ID,并将其作为字符串接受。 在
./schema/index.js
,在
Mutation
类型中编辑
createBook
,添加新的
Author
参数:
type Mutation {
createAuthor(name: String!): Author!
createBook(name: String!, pages: Int, author: String!): Book!
}
Then, in the same file, we’ll make a small change to the
createBook
resolver adding the author parameter to the function and as an argument to the
Book
constructor:
然后,在同一个文件中,我们对
createBook
解析器进行少量更改,将author参数添加到函数中,并作为
Book
构造函数的参数:
const resolvers = {
Query: {
authors: () => Author.find(),
books: () => Book.find()
},
Mutation: {
createAuthor: async (_, { name }) => {
const author = new Author({ name });
await author.save();
return author;
},
createBook: async (_, { name, pages, author }) => {
const book = new Book({ name, pages, author });
await book.save();
return book;
}
}
}
测试更新的模型和突变
(
Test the Updated Models and Mutations
)
Delete any old
Book
and
Author
instances in your MongoDB — they no longer reflect the updated schema. Then create a new
Author
. Run a query on the
Author
to get its ID — we’ll need this when creating a
Book
.
删除MongoDB中的所有旧
Book
和
Author
实例-它们不再反映更新后的架构。 然后创建一个新的
Author
。 在
Author
上运行查询以获取其ID-创建
Book
时将需要此ID。
As you can see, we created the new book without any errors. If we look in the Mongo database we can see that the book instance has an
Author
property with the
ObjectId
of our author.
如您所见,我们创建了新书,没有任何错误。 如果我们查看Mongo数据库,则可以看到该图书实例具有
Author
属性,该属性具有
Author
的
ObjectId
。
Unfortunately though, if we try to get the author details for the new book using our books query we’ll run into trouble.
但是,不幸的是,如果我们尝试使用图书查询来获取新书的作者详细信息,则会遇到麻烦。
解析器中的连接关系
(
Connecting Relations in the Resolvers
)
This part is fairly involved but quite exciting and we’ll end up using recursion.
这部分相当复杂,但非常令人兴奋,我们将最终使用递归。
As we know in MongoDB, we’re storing each book’s author property as an
ObjectId
string and each author’s books array is a list of
Book
ObjectId
s. Let’s turn these
ObjectId
s into something more useful.
正如我们在MongoDB中所知,我们将每本书的author属性存储为
ObjectId
字符串,而每位作者的books数组都是
Book
ObjectId
的列表。 让我们将这些
ObjectId
变成更有用的东西。
We’ll create two helper functions that will be used to expand a Book’s author
ObjectId
into an
Author
object and an
Author
’s array of book
ObjectId
s into an array of
Book
objects. I define these functions in the
./schema/index.js
file, just before the resolvers they’re going to be used in:
我们将创建两个帮助函数,这些函数将用于将Book的author
ObjectId
扩展为
Author
对象,并将
Author
的book
ObjectId
数组扩展为
Book
对象的数组。 我在
./schema/index.js
文件中定义了这些函数,就在解析器将要在以下函数中使用它们之前:
const books = async bookIds => {
try {
const books = await Book.find({_id: { $in: bookIds }})
return books.map(book => ({
...book._doc,
author: author.bind(this, book._doc.author)
}))
} catch {
throw err
}
}const author = async authorId => {
try {
const author = await Author.findById(authorId)
return {
...author._doc,
books: books.bind(this, author._doc.books)
}
} catch (err) {
throw err
}
}
As you can see, the books function accepts an array of
Book
ObjectId
s as an argument and then finds all the
Book
documents with those IDs. It then returns the
Book
s mapping over them and each
Book
returns all of its properties — except for the
Author
property. We overwrite the
Author
property when it’s requested by calling the
Author
function that’s been bound to the
Book
’s author’s
ObjectId
.
如您所见,books函数接受
Book
ObjectId
数组作为参数,然后查找具有这些ID的所有
Book
文档。 然后,它返回
Book
在它们之上的映射,每
Book
返回其所有属性-除了
Author
属性。 当请求时,我们通过调用绑定到
Book
作者的
ObjectId
的
Author
函数来覆盖
Author
属性。
The author function accepting an
Author
ObjectId
finds the
Author
document associated with that
ObjectId
and returns all of its properties, except for the books array. The books array is overwritten when that property is requested by calling the
Books
function, which returns all the properties for each book that has an associated
ObjectId
in the
Author
’s books array.
接受
Author
ObjectId
的author函数查找与该
ObjectId
关联的
Author
文档,并返回其所有属性(books数组除外)。 通过调用
Books
函数来请求该属性时,books数组将被覆盖,该函数将为
Author
的books数组中具有关联的
ObjectId
每本书返回所有属性。
We bind these functions for later use. If we were to call them immediately it would result in an infinite loop with the
Books
function calling the
Author
function, which would then call the
Books
function, and so on.
我们绑定这些功能以供以后使用。 如果我们立即调用它们,将导致
Books
函数调用
Author
函数的无限循环,然后再调用
Books
函数,依此类推。
We can now rewrite our
Query
resolvers using these handy functions to inflate a
Book
’s related
Author
and an
Author
’s related
Book
s:
现在,我们可以使用以下方便的函数来重写
Query
解析器,以使
Book
的相关
Author
和
Author
的相关
Book
膨胀:
const resolvers = {
Query: {
authors: async () => {
try {
const authors = await Author.find()
return authors.map(author => ({
...author._doc,
books: books.bind(this, author._doc.books)
}))
} catch (err) {
throw err
}
},
books: async () => {
try {
const books = await Book.find()
return books.map(book => ({
...book._doc,
author: author.bind(this, book._doc.author)
}))
} catch (err) {
throw err
}
}
},
There’s a lot of similarity in these queries to each other and the helper functions. The main difference from the helper functions is that we’re listing all books and all authors and using the helper functions to inflate their related properties on demand.
这些查询彼此之间以及辅助函数之间有很多相似之处。 与帮助程序功能的主要区别在于,我们列出了所有书籍和所有作者,并根据需要使用帮助程序功能来增加其相关属性。
We’ll finish off the resolver changes by rewriting the
Mutation
resolvers:
我们将通过重写
Mutation
解析器来完成解析器更改:
Const resolvers = {
Query: {
...
},
Mutation: {
createAuthor: async (_, { name }) => {
try {
const author = new Author({ name })
await author.save()
return author;
} catch (err) {
throw err
}
},
createBook: async (_, { name, pages, author: authorId }) => {
const book = new Book({ name, pages, author: authorId })
try {
const savedBook = await book.save()
const authorRecord = await Author.findById(authorId)
authorRecord.books.push(book)
await authorRecord.save()
return {
...savedBook._doc,
author: author.bind(this, authorId)
}
} catch (err) {
throw err
}
}
}
}
The
createAuthor
mutation remains unchanged except for adding the try/catch block.
除了添加try / catch块外,
createAuthor
突变保持不变。
In the
createBook
mutation we need to rename the author parameter that we supply as an argument from
author
to
authorId
as we are going to be using the
author
function and the names conflict. The most important change we’re making here is after the new
Book
has been saved we update the related
Author
by finding its document, pushing the new
Book
s
ObjectId
into the
Author
’s books array, and then saving the record. Just for good measure, we use our helper function to inflate the
Author
property on demand when the new
Book
is returned.
在
createBook
突变时,我们需要重命名我们提供的author参数作为
author
到
authorId
自变量,因为我们将使用
author
函数,并且名称发生冲突。 我们在这里所做的最重要的更改是在保存
Book
之后,我们通过查找相关文档,将
Book
的
ObjectId
推送到
Author
的books数组中来更新相关
Author
,然后保存记录。 出于很好的考虑,我们使用帮助函数在返回新
Book
时按需增加
Author
属性。
测试最终版本
(
Test the Final Version
)
We’ve made quite a few changes since the last testing so please delete any previously created
Author
s and
Book
s in your MongoDB and then create a new
Author
using the
createAuthor
mutation. Don’t forget to save the author’s ID. We’ll need that when creating
Book
s.
自上次测试以来,我们已经进行了很多更改,因此请删除MongoDB中以前创建的
Author
和
Book
,然后使用
createAuthor
突变创建一个新的
Author
。 不要忘记保存作者的ID。 创建
Book
时,我们将需要它。
Once that’s done we can see the changes by using the mutation
createBook
and asking for
Author
properties to be returned:
完成后,我们可以使用变体
createBook
并要求返回
Author
属性来查看更改:
Let’s create another
Book
using the same author but this time when returning the author for the new
Book
also return the names of all the books by that author:
让我们使用同一位作者创建另一
Book
,但是这次返回
Book
的作者时,还会返回该作者的所有书名:
Just to prove it all works, let’s use the
Author
s query to list all
Author
s and the names of their books:
为了证明这一切正常,让我们使用
Author
的查询列出所有
Author
及其书名:
However, I don’t recommend this as, apart from it being ridiculously pointless, it’s also rather slow.
但是,我不建议这样做,因为它除了荒谬无意义之外,而且速度也很慢。
As a final test to prove that the magic expanding the related properties is being done in the code and not in the background you can look in MongoDB. You should have an
Author
document with a
Books
array containing
ObjectId
s and some
Book
documents with
Author
properties that are also
ObjectId
s.
作为最终测试,证明扩展相关属性的魔力是在代码中完成的,而不是在后台完成的,您可以在MongoDB中查看。 您应该拥有一个带有一个包含
ObjectId
的
Books
数组的
Author
文档,以及一些带有
Author
属性也是
ObjectId
的
Book
文档。
The complete working code for this article
can be found here
.
可在此处找到
本文的完整工作代码。
翻译自:
https://medium.com/better-programming/how-to-add-relations-to-your-graphql-schema-295dd45cc62d
graphql
一、
graphql
基本语法:
https://
graphql
.cn/learn/queries/#variables
二、
graphql
-
java
(
graphql
java
客户端):
demo:https://blog.csdn.net/tangyaya8/article/details/105328461
扩展:数据关联
author对象下存在fans(一对多关联)
创建fans对象
@Entity
@Data
public class Fans {
Restful is Great! But
GraphQL
is Better. – My Humble Opinion.
GraphQL
will do to REST what JSON did to XML. – Samer Buna from Quora
GraphQL
作为 Facebook 的前端三架马车之一(另外两架是 Relay 和 React, 三者可以无缝结合),提出也有一段时间了,但真正用过的人却出奇的少,
截止到 2018 年底,根据 Stateofjs
https://edu.csdn.net/course/detail/36074
Python
实战量化交易理财系统
https://edu.csdn.net/course/detail/35475
使用Hot Chocolate和.NET 6构建
GraphQL
应用文章索引
在讨论完
GraphQL
中的查询需求后,这篇文章我们将演示如何实现
GraphQL
中的数据
添加
任务。
在
GraphQL
中,对数据进行查询使用query,而对于修改数据则需要使用muta
highlight: a11y-dark
GraphQL
既是一种用于 API 的查询语言也是一个满足你数据查询的运行时。
GraphQL
对你的 API 中的数据提供了一套易于理解的完整描述,使得客户端能够准确地获得它需要的数据,而且没有任何冗余。
1、安装依赖
npm install gq-loader --save-dev
2、React调用接口
封装api.js
export const client = new ApolloClient({
uri: postUrl,
GraphQL
并不会实现关联查询,数据关联需要程序自己实现
官网首页有介绍获取多个资源只需要一个请求,如想获取用户信息和身份证信息,原来需要先查用户信息,再通过用户id查询身份证信息,而在
GraphQL
中一次请求就可以实现。
对于这个观点我不敢苟同,可能我还没有体会到这种感觉,我认为只要需求明确,多个资源一次请求在RESTFUl中同样可以实现。
废话不说了,进入在正题
本文将从
GraphQL
是什么,为什么要使用
GraphQL
,使用
GraphQL
创建简单例子,以及
GraphQL
实战,四个方面对
GraphQL
进行阐述。说得不对的地方,希望大家指出斧正。
github项目地址:https://github.com/Charming2015/
graphql
-todolist
一、
GraphQL
是什么?
关于
GraphQL
是什么,网上一搜一大堆。根据官网的解释就是一...
GraphQL
服务端的库和应用可以用各种语言实现,所以这里的示例脱离语言。
GraphQL
服务端的应用代码的基本实现流程(需要提前安装好
GraphQL
库,各种语言的库参考这里):
定义用户自定义类型。类型的每个字段都必须是已定义的,且最终都是
GraphQL
中定义的类型。
定义根类型。每种根类型中包含了准备暴露给服务调用方的用
GraphQL
应用程序中的五个常见问题(以及如何修复它们)
学习解锁
GraphQL
的强大功能而不会遇到其缺点
GraphQL
现在风靡一时,并且有充分的理由:它是一种优雅的方法,可以解决许多与传统REST API相关的问题。
但是,如果我告诉你
GraphQL
没有自己的问题,那我就撒谎了。如果你不小心,这些问题可能不仅会导致代码库臃肿,甚至会导致应用程序显着减慢。
我在谈论的问题包括:
1.架构...