在 GraphQL 模式之间共享结构
Share structure between GraphQL schemas
我有一个 Apollo GraphQL 服务器与 API 返回的响应大致如下结构:
{
"pagination": {
"page": 1,
// more stuff
},
sorting: {
// even more stuff
},
data: [ // Actual data ]
}
此结构将在我广泛使用的 API 的几乎所有回复中共享。 data
大多数情况下是一个数组,但也可以是一个对象。
如何以高效的方式编写此代码,这样我就不必在我的架构中的每种数据类型上重复所有这些 pagination
和 sorting
字段?
非常感谢!
定义架构时,您最终会将分页、排序等抽象为单独的类型。所以模式看起来像:
type Bar {
pagination: Pagination
sorting: SortingOptions
data: BarData # I'm an object
}
type Foo {
pagination: Pagination
sorting: SortingOptions
data: [FooData] # I'm an array
}
# more types similar to above
type Pagination {
page: Int
# more fields
}
type SortingOptions {
# more fields
}
type BarData {
# more fields
}
因此,无论如何,您都不必多次列出分页中的每个字段。但是,每种使用分页的类型仍需要将其指定为一个字段——这一要求无可避免。
或者,您可以设置一个类型以用于所有对象。在这种情况下,数据字段将是一个接口(数据),每个接口都有 FooData、BarData 等实现它。在您的数据解析器中,您将定义一个 __resolveType 函数来确定要 return 的数据类型。您可以在查询中传递类型名称变量,然后在 __resolveType 函数中使用该变量来 return 正确的类型。
您可以在 Apollo docs.
中看到一个很好的接口示例
后一种方法的缺点是您必须 return 单个数据对象或它们的数组——您不能混合和匹配——因此您可能必须更改returned 对象的结构以使其工作。
我已经通过创建一个名为 graphql-s2s 的库解决了你的问题。它通过添加对类型继承、通用类型和元数据的支持来增强您的架构。在您的情况下,为您的分页对象创建一个泛型类型可能是一个可行的解决方案。这是一个例子:
const { transpileSchema } = require('graphql-s2s')
const { makeExecutableSchema } = require('graphql-tools')
const schema = `
type Paged<T> {
data: [T]
cursor: ID
}
type Node {
id: ID!
creationDate: String
}
type Person inherits Node {
firstname: String!
middlename: String
lastname: String!
age: Int!
gender: String
}
type Teacher inherits Person {
title: String!
}
type Student inherits Person {
nickname: String!
questions: Paged<Question>
}
type Question inherits Node {
name: String!
text: String!
}
type Query {
students: Paged<Student>
teachers: Paged<Teacher>
}
`
const executableSchema = makeExecutableSchema({
typeDefs: [transpileSchema(schema)],
resolvers: resolver
})
我已经写了关于这个的更多细节here(在第二部分)。
我有一个 Apollo GraphQL 服务器与 API 返回的响应大致如下结构:
{
"pagination": {
"page": 1,
// more stuff
},
sorting: {
// even more stuff
},
data: [ // Actual data ]
}
此结构将在我广泛使用的 API 的几乎所有回复中共享。 data
大多数情况下是一个数组,但也可以是一个对象。
如何以高效的方式编写此代码,这样我就不必在我的架构中的每种数据类型上重复所有这些 pagination
和 sorting
字段?
非常感谢!
定义架构时,您最终会将分页、排序等抽象为单独的类型。所以模式看起来像:
type Bar {
pagination: Pagination
sorting: SortingOptions
data: BarData # I'm an object
}
type Foo {
pagination: Pagination
sorting: SortingOptions
data: [FooData] # I'm an array
}
# more types similar to above
type Pagination {
page: Int
# more fields
}
type SortingOptions {
# more fields
}
type BarData {
# more fields
}
因此,无论如何,您都不必多次列出分页中的每个字段。但是,每种使用分页的类型仍需要将其指定为一个字段——这一要求无可避免。
或者,您可以设置一个类型以用于所有对象。在这种情况下,数据字段将是一个接口(数据),每个接口都有 FooData、BarData 等实现它。在您的数据解析器中,您将定义一个 __resolveType 函数来确定要 return 的数据类型。您可以在查询中传递类型名称变量,然后在 __resolveType 函数中使用该变量来 return 正确的类型。
您可以在 Apollo docs.
中看到一个很好的接口示例后一种方法的缺点是您必须 return 单个数据对象或它们的数组——您不能混合和匹配——因此您可能必须更改returned 对象的结构以使其工作。
我已经通过创建一个名为 graphql-s2s 的库解决了你的问题。它通过添加对类型继承、通用类型和元数据的支持来增强您的架构。在您的情况下,为您的分页对象创建一个泛型类型可能是一个可行的解决方案。这是一个例子:
const { transpileSchema } = require('graphql-s2s')
const { makeExecutableSchema } = require('graphql-tools')
const schema = `
type Paged<T> {
data: [T]
cursor: ID
}
type Node {
id: ID!
creationDate: String
}
type Person inherits Node {
firstname: String!
middlename: String
lastname: String!
age: Int!
gender: String
}
type Teacher inherits Person {
title: String!
}
type Student inherits Person {
nickname: String!
questions: Paged<Question>
}
type Question inherits Node {
name: String!
text: String!
}
type Query {
students: Paged<Student>
teachers: Paged<Teacher>
}
`
const executableSchema = makeExecutableSchema({
typeDefs: [transpileSchema(schema)],
resolvers: resolver
})
我已经写了关于这个的更多细节here(在第二部分)。