graphql tools

I’ve been working with GraphQL for a few months now, but only recently began using Apollo’s graphql-tools library. After learning a few idioms, I am able to mock up a functional API quickly. This is largely due to its low-code, declarative approach to type definitions.

我已经使用GraphQL已有几个月了,但是直到最近才开始使用Apollo的graphql-tools库。 学习了一些习惯用法后,我便能够快速模拟出功能性的API。 这主要是由于其对类型定义的低代码声明性方法。

从他们的例子开始 (Starting with their example)

Apollo has an interactive LaunchPad web site, like the ones covered in my Swagger series. There are several example schemas you can use, and for this article I will use their Post and Authors schema. You can download or fork the code.

阿波罗(Apollo)有一个交互式LaunchPad网站,就像我的Swagger系列文章中介绍的网站一样。 您可以使用几种示例模式,在本文中,我将使用其Post and Authors模式 。 您可以下载或分叉代码。

I will be rearranging the project folders. For this post I’ll download and store it in Github, so I can branch and modify the code through each step. Along the way, I’ll link the branches to this post.

我将重新排列项目文件夹。 对于这篇文章,我将其下载并存储在Github中,因此我可以在每个步骤中分支和修改代码。 一路上,我将分支链接到该帖子。

基础 (The basics)

  • declaring schema types

    声明架构类型

In the Launchpad, you’ll see a typeDefs template literal:

在启动板中,您将看到typeDefs模板文字:

const typeDefs = `type Author {id: Int!firstName: StringlastName: Stringposts: [Post] # the list of Posts by this author}type Post {id: Int!title: Stringauthor: Authorvotes: Int}# the schema allows the following query:type Query {posts: [Post]author(id: Int!): Author}# this schema allows the following mutation:type Mutation {upvotePost (postId: Int!): Post}
`;

There are two entities defined, Author and Post. In addition, there are two “magic” types: Query and Mutation. The Query type defines the root accessors. In this case, there’s an accessor to fetch all Posts, and another to fetch a single Author by ID.

定义了两个实体AuthorPost 。 此外,还有两种“魔术” 类型QueryMutation 。 查询类型定义根accessors 。 在这种情况下,有一个访问器来获取所有Posts ,另一个访问器是通过ID获取单个Author

Note there is no way to directly query for a list of authors or for a single post. It is possible to add such queries later.

请注意,无法直接查询作者列表或单个帖子。 以后可以添加此类查询。

  • declaring resolvers

    宣布解析器

Resolvers provide the necessary logic to support the schema. They are written as a JavaScript object with keys that match the types defined in the schema. The resolver shown below operates against static data, which I’ll cover in a moment.

解析程序提供了支持架构的必要逻辑。 它们被编写为具有与模式中定义的类型相匹配的键JavaScript对象。 下面显示的resolver针对静态数据进行操作,我将在稍后介绍。

const resolvers = {Query: {posts: () => posts,author: (_, { id }) => find(authors, { id: id }),},Mutation: {upvotePost: (_, { postId }) => {const post = find(posts, { id: postId });if (!post) {throw new Error(`Couldn't find post with id ${postId}`);}post.votes += 1;return post;},},Author: {posts: (author) => filter(posts, { authorId: author.id }),},Post: {author: (post) => find(authors, { id: post.authorId }),},
};

To link schema and resolver together, we’ll create an executable schema instance:

要将schemaresolver链接在一起,我们将创建一个可执行架构实例:

export const schema = makeExecutableSchema({typeDefs,resolvers,
});
  • the data source

    数据源

For this simple example, the data comes from two arrays of objects defined as constants: authors and posts:

对于此简单示例,数据来自定义为常量的两个对象数组: authorsposts

const authors = [{ id: 1, firstName: 'Tom', lastName: 'Coleman' },{ id: 2, firstName: 'Sashko', lastName: 'Stubailo' },{ id: 3, firstName: 'Mikhail', lastName: 'Novikov' },
];const posts = [{ id: 1, authorId: 1, title: 'Introduction to GraphQL', votes: 2 },{ id: 2, authorId: 2, title: 'Welcome to Meteor', votes: 3 },{ id: 3, authorId: 2, title: 'Advanced GraphQL', votes: 1 },{ id: 4, authorId: 3, title: 'Launchpad is Cool', votes: 7 },
];
  • the server

    服务器

You can serve up the executable schema through graphql_express, apollo_graphql_express, or graphql-server-express. We see that in this example.

您可以通过graphql_expressapollo_graphql_expressgraphql-server-express提供可执行模式 我们在这个例子中看到了。

The important bits are:

重要的位是:

import { graphqlExpress, graphiqlExpress } from 'graphql-server-express';
import { schema, rootValue, context } from './schema';const PORT = 3000;
const server = express();server.use('/graphql', bodyParser.json(), graphqlExpress(request => ({schema,rootValue,context: context(request.headers, process.env),
})));server.use('/graphiql', graphiqlExpress({endpointURL: '/graphql',
}));server.listen(PORT, () => {console.log(`GraphQL Server is now running on
http://localhost:${PORT}/graphql`);console.log(`View GraphiQL at
http://localhost:${PORT}/graphiql`);
});

Note that there are two pieces of GraphQL middleware in use:

请注意,有两个正在使用的GraphQL中间件:

  • graphqlExpress

    graphqlExpress

    the GraphQL server that handles queries and responses

    处理查询和响应的GraphQL服务器

  • graphiqlExpress

    graphiqlExpress

    the interactive GraphQL web service that allows interactive queries through an HTML UI

    交互式GraphQL Web服务,该服务允许通过HTML UI进行交互式查询

改组 (Reorganizing)

For large apps, we suggest splitting your GraphQL server code into 4 components: Schema, Resolvers, Models, and Connectors, which each handle a specific part of the work. (http://dev.apollodata.com/tools/graphql-tools/)

对于大型应用程序,我们建议将GraphQL服务器代码分成4个组件:架构,解析器,模型和连接器,它们分别处理工作的特定部分。 ( http://dev.apollodata.com/tools/graphql-tools/ )

Putting each type of component in its own file makes sense. I’ll go one better and put each set of components in a its own “domain” folder.

将每种类型的组件放在其自己的文件中是有意义的。 我会做得更好,并将每组组件放在一个自己的“域”文件夹中。

为什么要域名? (Why domains?)

Domains are a convenient way to split up a large system into areas of operation. Within each domain there may be subdomains. In general, subdomains have a bounded context. Within a bounded context the entity names, properties, and processes have precise meaning.

域是将大型系统划分为多个操作区域的便捷方法。 在每个域中可能有子域。 通常,子域具有有限的上下文。 在有限的上下文中,实体名称,属性和过程具有精确的含义。

I find bounded contexts to be helpful during analysis, especially when talking to domain experts.

我发现有限的上下文在分析过程中会有所帮助,特别是在与领域专家交谈时。

The fly in the ointment is that GraphQL types occupy a single namespace, so naming conflicts can exist. More on that later.

美中不足的是,GraphQL类型仅占用一个名称空间,因此可能存在命名冲突。 以后再说。

I’ll call this domain authorposts, and put the related components in the authorposts folder. Within that, I’ll create a file each for datasource, resolvers, and schema. Let’s also toss in an index.js file to simplify importing. The original schema and server files will remain in the root folder, but the schema.js code will be skeletal. The find and filter methods imported from lodash will be removed in favor of synonymous native ES6 methods. The resulting source is here.

我将这个域称为authorposts ,并将相关组件放入authorposts folder authorposts folder authorposts folder 。 在其中,我将分别为datasourceresolvers和schema创建一个文件。 让我们也将index.js文件折腾以简化导入。 原始模式和服务器文件将保留在根文件夹中,但是schema.js代码将是骨架的。 findlodash导入的filter方法将被删除,以支持同义的本机ES6方法。 结果来源在这里 。

The main schema file has become simpler. It provides skeletal structure for further extension by schemas in our domains.

主模式文件变得更加简单。 它为我们的领域中的架构提供了进一步扩展的骨架结构。

A domain schema is imported on lines 7–8, and the base schema on lines 11–23. You’ll note there is a domain property. This is arbitrary but GraphQL, or graphql-tools, insists that one property be defined.

domain模式在第7–8行导入, base模式在第11–23行导入。 您会注意到有一个属性。 这是任意的,但是GraphQL或graphql-tools坚持要定义一个属性。

The complete schema is constructed on line 26, and an executableSchema instance is created given the schema and resolvers defined so far on lines 28–33. This is what is imported by the server.js code, which is largely unchanged from the original.

完整的架构在第26行上构建,并根据第28–33行到目前为止定义的schemaresolvers创建了一个executableSchema实例。 这就是server.js代码导入的内容,与原始代码基本没有变化。

There is a trick to splitting up a schema this way. Let’s take a look:

有这样一种技巧可以拆分模式。 让我们来看看:

The first listing, authorpostResolvers.js, is pretty much a cut’n’paste job from the original schema.js source from Apollo’s example. Yet in the authorpostSchema.js code, we extend the Query and Mutator definitions that are declared in the the base schema. If you don’t use the extend keyword, the executable schema builder will complain about two Query definitions.

第一个清单authorpostResolvers.js是Apollo示例中原始schema.js源代码中的一个“剪切”粘贴工作。 但是,在authorpostSchema.js代码中,我们扩展了在基础架构中声明的QueryMutator定义。 如果不使用extend关键字,则可执行模式构建器将抱怨两个查询定义。

继续… (Continuing…)

This is a good start for organizing several schemas, one for each domain of interest (so long as you're mindful of the global namespace for types), but a complete schema, even for a single domain, can get huge. Fortunately, you can break down each schema even further, right down to the entity level, if necessary.

这是组织多个模式的一个良好的开始,一个模式用于每个感兴趣的域(只要您注意类型的全局名称空间),但是即使是单个域,一个完整的模式也会变得庞大。 幸运的是,您可以根据需要甚至进一步细分每个架构,直至实体级别 。

Here’s a modified directory structure, and listings of the new contents:

这是修改后的目录结构,并列出了新内容:

We can achieve granularity by defining two component files, then importing them into a domain schema.

我们可以通过定义两个组件文件,然后将它们导入域模式来实现粒度。

You don’t have to do one component per file. But you do want to be sure that the schema exports those components along with the schema itself as shown on line 20 of schema.js. Otherwise you’ll likely wind up missing a dependency further down the inclusion chain.

您不必为每个文件做一个组件。 但是您确实要确保该模式将这些组件与模式本身一起导出,如schema.js的第20行所示。 否则,您很可能最终会在包含链的下方错过一个依赖项。

多个架构和解析器 (Multiple schemas and resolvers)

Adding a new schema for a new domain is simple. Create a new domain folder and add dataSource, resolvers, schema, and index.js files. You can also add an optional component folder with component type definitions.

为新域添加新架构很简单。 创建一个新的域文件夹,并添加dataSource,解析器,架构和index.js文件。 您还可以添加带有组件类型定义的可选组件文件夹。

Finally, the root schema.js file must combine the schemas and resolvers from both domains:

最后,根schema.js文件必须结合两个域中的模式和解析器:

//...
import {schema as myLittleTypoSchema,resolvers as myLittleTypeResolvers
} from './myLittleDomain';import {merge
} from 'lodash';
//...
const schema = [...baseSchema, ...authorpostsSchema, ...myLittleTypoSchema]const options = {typeDefs: schema,resolvers: merge(authorpostsResolvers, myLittleTypeResolvers)
}

Note that I had to include lodash merge here because of the need for a deep merge of the two resolvers imports.

请注意,由于必须深度合并两个解析器 ,因此我必须在此处包括lodash 合并 进口。

处理命名空间冲突 (Dealing with Namespace Collisions)

If you are on a large project, you will encounter type name collisions. You might think that Account in one domain would mean the same as Account in another. Yet even if they do mean more or less similar things, chances are the properties and relationships will be different. So technically they are not the same type.

如果您在大型项目中,则会遇到类型名称冲突。 您可能会认为一个域中的帐户与另一个域中的帐户含义相同。 然而,即使它们确实或多或少地意味着相似的事物,属性和关系也有可能会不同。 因此,从技术上讲,它们不是同一类型。

At the time of this writing, GraphQL uses a single namespace for types.

在撰写本文时,GraphQL对类型使用单个名称空间。

How to work around this? Facebook apparently uses a naming convention for their 10,000 types. As awkward as that seems, it works for them.

如何解决这个问题? Facebook显然为其10,000种类型使用命名约定 。 看起来很尴尬,但对他们有用。

The Apollo graphql-tools stack appears to catch type name duplications. So you should be good there.

Apollo graphql-tools堆栈似乎捕获类型名称重复项。 所以你在那里应该很好。

There is an ongoing discussion on whether to include namespaces in GraphQL. It isn’t a simple decision . I remember the complexities caused by the introduction of XML Namespaces 10 years ago.

关于是否在GraphQL中包括名称空间的讨论正在进行中。 这不是一个简单的决定。 我记得10年前引入XML命名空间引起的复杂性。

然后去哪儿? (Where to go from here?)

This post only scratches the surface of how one might organize a large set of GraphQL schemas. The next post will be about mocking GraphQL resolvers, and how it’s possible to mix both real and mocked values in query responses.

这篇文章只是对如何组织一大套GraphQL模式的表述。 下一篇文章将关于模拟GraphQL解析器,以及如何在查询响应中混合使用真实值和模拟值。

翻译自: https://www.freecodecamp.org/news/declarative-graphql-with-graphql-tools-cd1645f94fc/

graphql tools

graphql tools_声明式GraphQL:编写更少的代码,并使用graphql-tools完成更多工作相关推荐

  1. lombok 简化代码_如何编写更少,更好的代码,或Project Lombok

    lombok 简化代码 我长期以来一直打算写有关Lombok的文章 ,以至于每当一个自重的Java开发人员都已经听说过它时,我可能会写它. 尽管如此,值得一提的是,如果只是提醒自己,不要犹豫,尝试性能 ...

  2. 如何编写更少,更好的代码,或Project Lombok

    我长期以来一直打算撰写有关Lombok项目的文章 ,以至于每当一个自重的Java开发人员都已经听说过它时,我可能会写它. 尽管如此,值得一提的是,如果只是提醒自己,不要犹豫,尝试性能增强工具并查看它们 ...

  3. 编写更少量的代码:使用apache commons工具类库

    Commons-configuration Commons-FileUploadCommons DbUtilsCommons BeanUtils Commons CLI Commons CodecCo ...

  4. 译 | 使用Roslyn分析器高效编写更好的代码

    原文:Mika Dumont 翻译:汪宇杰 Roslyn 是 .NET 编译器平台,即使在运行代码之前,它也能帮助您捕获 Bug.例如内置于 Visual Studio 中的Roslyn 拼写检查分析 ...

  5. 更快地编写更好的代码:5 分钟阅读

    更快地编写更好的代码:5 分钟阅读 关于如何在不影响质量的情况下更快地编码的小技巧 最近,我阅读了David Farley的"现代软件工程",它详细阐述了现代软件开发核心的持久性原 ...

  6. lombok和maven_Lombok,AutoValue和Immutables,或如何编写更少,更好的代码返回

    lombok和maven 在上一篇有关Lombok库的文章中 ,我描述了一个库,该库有助于处理Java中的样板代码( 是的,我知道这些问题已经在Kotlin中解决了 ,但这是现实生活,我们不能一味地坐 ...

  7. Lombok,AutoValue和Immutables,或如何编写更少,更好的代码返回

    在上一篇有关Lombok库的文章中 ,我描述了一个库,该库有助于处理Java中的样板代码( 是的,我知道这些问题已经在Kotlin中解决了 ,但这是现实生活,我们不能一味地坐下来,一旦出现较新或更简单 ...

  8. 前端工程化精讲第七课 低代码工具:如何用更少的代码实现更灵活的需求?

    在进入到这一课的内容之前先让我们来回顾下,通过脚手架工具生成初始化代码,以及通过 Snippet 工具生成代码片段的方式.两种方案的相同在于,通过简单的输入和选择就能获得预设后的项目代码.这种转化方式 ...

  9. 编写react组件_React组件的“黄金法则”如何帮助您编写更好的代码

    编写react组件 以及钩子如何发挥作用 (And how hooks come into play) Recently I've adopted a new philosophy that chan ...

最新文章

  1. OpenCV SVM支持向量机和KNearest数字识别的实例(附完整代码)
  2. 【贪心】最佳浏览路线问题
  3. java 修改 jar 包文件内容的一个实例
  4. Android开发与Sequoyah的安装问题
  5. 4G模组工作笔记001---NB-IOT之一个完整的BC95 UDP从开机到数据发送接收过程
  6. Linux驱动编写(块设备驱动代码)
  7. 在cmd里面运行adb命令的时候提示:adb server is out of date. killing...
  8. [Luogu P2893][USACO08FEB]修路Making the Grade
  9. java静态成员静态代码块初始化顺序
  10. 网购心脏起搏器存在多达8000个程序漏洞
  11. 宏碁4743G+固态硬盘(SSD)+机械硬盘(HHD)混合用
  12. 中英文常用标点符号统一清洗为英文格式
  13. SSDT表函数Hook原理
  14. 《系统集成项目管理》第三章 信息系统集成专业技术知识
  15. 5G+工业互联网发展探讨
  16. 高通量测序与杂交优势
  17. 重装w11新硬盘,找不到硬盘
  18. JS中终止函数执行的代码
  19. Photoshop 使用钢笔工具 进行抠图 商业出图入门
  20. 从软件工程师到IT猎头:我的一点经历和感触 (转)

热门文章

  1. 想学IT的必看!今年Android面试必问的这些技术面,架构师必备技能
  2. PTA -- A1046 Shortest Distance
  3. 这几天微软发布的一些好玩的东西(顺祝女性程序员朋友们节日快乐!)
  4. JAVA的值传递问题
  5. es6解决回调地狱问题
  6. truncate 、delete与drop区别
  7. python基础课程_2学习笔记3:图形用户界面
  8. 常见开源分布式存储系统
  9. Iphone表视图的简单操作
  10. VC++的windows服务