Мутация GraphQL, которая принимает массив динамического размера и общие скалярные типы в одном запросе

Мне нужно иметь возможность создать пользователя и добавить его любимые фильмы (массив объектов со ссылкой на коллекцию фильмов и его личный рейтинг для каждого фильма) в одном запросе.

Нечто похожее на это (псевдокод)

var exSchema = `
 type Mutation {
    addUser(
        name: String!
        favMovies: [{ movie: String! #ref to movies coll
                      personal_rating: Int! # this is different for every movie
                   }]
    ) : User
 }
...
`

Что такое GraphQL способ сделать это в одном запросе? Я знаю, что могу достичь результата с помощью нескольких мутаций / запросов, но я бы хотел сделать это за один раз.

6 ответов

Решение

Вы можете передать массив, как это

var MovieSchema = `
  type Movie {
   name: String
  }
  input MovieInput {
   name: String
  }
  mutation {
   addMovies(movies: [MovieInput]): [Movie]
  }
`

Затем в вашей мутации, вы можете передать массив как

mutation {
  addMovies(movies: [{name: 'name1'}, {name: 'name2'}]) {
    name
  }
}

Не проверял код, но вы поняли идею

Я придумал это простое решение - НЕТ JSON. Используется только один вход. Надеюсь, это поможет кому-то еще.

Я должен был добавить к этому типу:

type Option {
    id: ID!
    status: String!
    products: [Product!]!
}

Мы можем добавить к типу мутации и добавить ввод следующим образом:

type Mutation {
    createOption(data: [createProductInput!]!): Option!
    // other mutation definitions
}

input createProductInput {
    id: ID!
    name: String!
    price: Float!
    producer: ID!
    status: String
}

Тогда можно использовать следующий резольвер:

const resolvers = {
    Mutation: {
      createOption(parent, args, ctx, info) {

        const status = args.data[0].status;

        // Below code removes 'status' from all array items not to pollute DB.
        // if you query for 'status' after adding option 'null' will be shown. 
        // But 'status': null should not be added to DB. See result of log below.
        args.data.forEach((item) => {
            delete item.status
        });

        console.log('args.data - ', args.data);

        const option = {
            id: uuidv4(),
            status: status,  // or if using babel    status,
            products: args.data
        }

        options.push(option)

        return option
      },
    // other mutation resolvers
    }

Теперь вы можете использовать это, чтобы добавить опцию (STATUS берется из первого элемента в массиве - это можно обнулять):

mutation{
  createOption(data:
    [{
            id: "prodB",
            name: "componentB",
            price: 20,
            producer: "e4",
            status: "CANCELLED"
        },
        {
            id: "prodD",
            name: "componentD",
            price: 15,
            producer: "e5"
        }
    ]
  ) {
    id
    status
    products{
      name
      price
    }
  }
}

Производит:

{
  "data": {
    "createOption": {
      "id": "d12ef60f-21a8-41f3-825d-5762630acdb4",
      "status": "CANCELLED",
      "products": [
        {
          "name": "componentB",
          "price": 20,
        },
        {
          "name": "componentD",
          "price": 15,
        }
      ]
    }
  }
}

Не нужно говорить, что для получения вышеуказанного результата нужно добавить:

type Query {
    products(query: String): [Product!]!
    // others
}

type Product {
    id: ID!
    name: String!
    price: Float!
    producer: Company!
    status: String
}

Я знаю, что это не лучший способ, но я не нашел способа сделать это в документации.

В итоге я вручную проанализировал правильную схему, поскольку массивы JavaScript и строки JSON.stringify не были приняты как формат схемы graphQL.

const id = 5;
const title = 'Title test';

let formattedAttachments = '';
attachments.map(attachment => {
  formattedAttachments += `{ id: ${attachment.id}, short_id: "${attachment.shortid}" }`;      
  // { id: 1, short_id: "abcxyz" }{ id: 2, short_id: "bcdqrs" }
});

// Query
const query = `
  mutation {
    addChallengeReply(
      challengeId: ${id}, 
      title: "${title}", 
      attachments: [${formattedAttachments}]
    ) {
      id
      title
      description
    }
  }
`;

Что я понимаю под вашим требованием, так это то, что если у вас есть следующий код

const user = {
    name:"Rohit", 
    age:27, 
    marks: [10,15], 
    subjects:[
        {name:"maths"},
        {name:"science"}
    ]
};

const query = `mutation {
        createUser(user:${user}) {
            name
        }
}`

у тебя должно быть что-то вроде

"mutation {
        createUser(user:[object Object]) {
            name
        }
}"

вместо ожидаемого

"mutation {
        createUser(user:{
            name: "Rohit" ,
            age: 27 ,
            marks: [10 ,15 ] ,
            subjects: [
                {name: "maths" } ,
                {name: "science" } 
                ] 
            }) {
            name
        }
}"

Если это то, чего вы хотели достичь, то gqlast - хорошая функция тега, которую вы можете использовать для получения ожидаемого результата.

Просто возьмите отсюда файл js и используйте его как:

const user = {
    name:"Rohit", 
    age:27, 
    marks: [10,15], 
    subjects:[
        {name:"maths"},
        {name:"science"}
    ]
};

const query = gqlast`mutation {
        createUser(user:${user}) {
            name
        }
}`

Результат сохраняется в переменной query будет:

"mutation {
        createUser(user:{
            name: "Rohit" ,
            age: 27 ,
            marks: [10 ,15 ] ,
            subjects: [
                {name: "maths" } ,
                {name: "science" } 
                ] 
            }) {
            name
        }
}"

Передайте их как строки JSON. Это то, чем я занимаюсь.

Для тех из вас, кому не нужно передавать массив для одного запроса, и кто открыт для идеи делать запрос для каждой мутации. (Я использую Vue3, сборку Api, но разработчики React и Angular все еще могут это понять).

Вы не можете зациклить мутацию следующим образом:

      function createProject() {
    for (let i = 0; i < state.arrOfItems.length; i++) {
      const { mutate: addImplementation } = useMutation(
        post_dataToServer,
        () => ({
          variables: {
            implementation_type_id: state.arrOfItems[i],
            sow_id: state.newSowId,
          },
        })
      );

      addImplementation();
    }
}

это приведет к ошибке, потому что мутация должна быть в setup ().(вот ошибка, которую вы получите: https://github.com/vuejs/vue-apollo/issues/888)

Вместо этого создайте дочерний компонент и сопоставьте массив с родительским.

в Parent.vue

      <div v-for="(card, id) in state.arrOfItems">
  <ChildComponent
    :id="id"
    :card="card"
  />
</div>

в ChildComponent.vue

получить реквизит и:

       const { mutate: addImplementation } = useMutation(
    post_dataToServer,
    () => ({
      variables: {
        implementation_id: props.arrOfItems,
        id: props.id,
      },
    })
  );
Другие вопросы по тегам