Вот мои требования:
"добавить" мутацию, каждое поле (или называемое скаляром) входного типа BookInput должно иметь дополнительные модификаторы типа "!" для проверки ненулевого значения. Это означает, что когда я добавляю книгу, в аргументе должны быть поля title и author, например {title: "angular", author: "novaline"}.
"обновить" мутацию, я хочу обновить часть полей книги, не хочу обновлять всю книгу (документ MongoDB, и я не хочу, чтобы интерфейсный интерфейс передавал серверу graphql аргумент о мутации всей большой книги для экономия полосы пропускания). Это означает, что аргумент книги может быть {title: "angular"} или {title: "angular", author: "novaline"}.
Вот мои определения типов:
const typeDefs = `
input BookInput {
title: String!
author: String!
}
type Book {
id: ID!
title: String!
author: String!
}
type Query {
books: [Book!]!
}
type Mutation{
add(book: BookInput!): Book
update(id: String!, book: BookInput!): Book
}
`;
На данный момент "добавить" мутацию работает нормально. Но мутация "update" не может пройти проверку на ненулевое значение, если я передаю аргумент {title: "angular"}.
Вот мутация, которая не проходит проверку на ненулевое значение, отсутствие поля «автор» для типа ввода BookInput.
mutation {
update(id: "1", book: {title: "angular"}) {
id
title
author
}
}
Итак, graphql выдаст мне ошибку:
{
"errors": [
{
"message": "Field BookInput.author of required type String! was not provided.",
"locations": [
{
"line": 2,
"column": 24
}
]
}
]
}
Как мне спроектировать входной тип BookInput? Не хочу определять addBookInput и updateBookInput. Это продублировано.


Очень распространенный шаблон - иметь отдельные типы входных данных для каждой мутации. Вы также можете создать один запрос на мутацию для каждой операции. Возможно что-то вроде этого:
const typeDefs = `
input AddBookInput {
title: String!
author: String!
}
input UpdateBookInput {
# NOTE: all fields are optional for the update input
title: String
author: String
}
type Book {
id: ID!
title: String!
author: String!
}
type Query {
books: [Book!]!
}
type Mutation{
addBook(input: AddBookInput!): Book
updateBook(id: String!, input: UpdateBookInput!): Book
}
`;
Некоторым людям также нравится включать идентификатор обновления как часть ввода обновления:
const typeDefs = `
input AddBookInput {
title: String!
author: String!
}
input UpdateBookInput {
# NOTE: all fields, except the 'id' (the selector), are optional for the update input
id: String!
title: String
author: String
}
type Book {
id: ID!
title: String!
author: String!
}
type Query {
books: [Book!]!
}
type Mutation{
addBook(input: AddBookInput!): Book
updateBook(input: UpdateBookInput!): Book
}
`;
Наконец, вы можете захотеть использовать тип «полезная нагрузка» для типа возвращаемого значения - для дополнительной гибкости (дает вам больше возможностей для маневра, чтобы изменить тип возвращаемого значения позже, не нарушая ваш API):
const typeDefs = `
input AddBookInput {
title: String!
author: String!
}
input UpdateBookInput {
# NOTE: all fields, except the 'id' (the selector), are optional for the update input
id: String!
title: String
author: String
}
type Book {
id: ID!
title: String!
author: String!
}
type AddBookPayload {
book: Book!
}
type UpdateBookPayload {
book: Book!
}
type Query {
books: [Book!]!
}
type Mutation{
addBook(input: AddBookInput!): AddBookPayload!
updateBook(input: UpdateBookInput!): UpdateBookPayload!
}
`;
Надеюсь это поможет!
Вот мое решение: я пишу вспомогательную функцию для создания типа «создать» input и «обновить» тип input.
const { parse } = require('graphql');
/**
* schema definition helper function - dynamic generate graphql input type
*
* @author https://github.com/mrdulin
* @param {string} baseSchema
* @param {object} options
* @returns {string}
*/
function generateInputType(baseSchema, options) {
const inputTypeNames = Object.keys(options);
const schema = inputTypeNames
.map(inputTypeName => {
const { validator } = options[inputTypeName];
const validatorSchema = Object.keys(validator)
.map(field => `${field}: ${validator[field]}\n`)
.join(' ');
return `
input ${inputTypeName} {
${baseSchema}
${validatorSchema}
}
`;
})
.join(' ')
.replace(/^\s*$(?:\r\n?|\n)/gm, '');
parse(schema);
return schema;
}
schema.js:
${generateInputType(
`
campaignTemplateNme: String
`,
{
CreateCampaignTemplateInput: {
validator: {
channel: 'ChannelUnionInput!',
campaignTemplateSharedLocationIds: '[ID]!',
campaignTemplateEditableFields: '[String]!',
organizationId: 'ID!',
},
},
UpdateCampaignTemplateInput: {
validator: {
channel: 'ChannelUnionInput',
campaignTemplateSharedLocationIds: '[ID]',
campaignTemplateEditableFields: '[String]',
organizationId: 'ID',
},
},
},
)}