graphql,当有“添加”和“更新”变异时如何设计输入类型?

问题描述 投票:2回答:2

这是我的要求:

  1. “添加”变异,BookInput输入类型的每个字段(或称为标量)都应该有其他类型修饰符“!”验证非空值。这意味着当我添加一本书时,参数必须有titleauthor字段,如{title: "angular", author: "novaline"}
  2. “更新”变种,我想更新本书的一部分字段,不想更新整本书(MongoDB文件,而且,我不希望前端通过graphql server整个大书变异论证为节省带宽)。这意味着书的论点可以是{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
  }
`;

目前,“添加”突变工作正常。但是,如果我通过{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输入类型?不想定义addBookInputupdateBookInput。这是重复的。

graphql graphql-js apollo-server
2个回答
6
投票

一种非常常见的模式是每个突变都有不同的输入类型。您可能还希望为每个操作创建一个突变查询。也许是这样的:

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
  }
`;

有些人还希望将更新ID作为更新输入的一部分包含在内:

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!
  }
`;

希望这可以帮助!


0
投票

这是我的解决方案,我编写了一个辅助函数来生成“创建”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, '');

  try {
    parse(schema);
    return schema;
  } catch (err) {
    throw new Error(`${err.message}`);
  }
}

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',
      },
    },
  },
)}
© www.soinside.com 2019 - 2024. All rights reserved.