What is the correct way to `connect` multiple instances of a `type` and return it multiple times?

prisma

#1

I’m not sure what the best way to ask this question is, but here goes nothing…

I’m building my first prisma/graphql-yoga backend application to learn more about it. The project idea is a martial arts practice app. It’s going pretty well so far, but I’m stuck on one particular thing (for the moment).

What is the correct way to connect multiple instances of a type and return it multiple times?

Consider two types: Technique and Combination. A Technique is a specific movement (e.g. a punch, kick, etc…) and a Combination is a group of those techniques.

To model this, my datamodel.prisma is

type Technique {
  id: ID! @unique
  name: String! @unique
  description: String
  rank: Rank!
}

type Combination {
  id: ID! @unique
  name: String! @default(value: "Untitled")
  numTechniques: Int @default(value: "1")
  # make a connection to the Technique type
  techniques: [Technique!]!
  maxRank: Rank! @default(value: "NONE")
}

The relevant part of my schema.graphql looks like this

input TechniqueInput {
  id: ID!
}

type Mutation {
  createTechnique(
    name: String! 
    description: String 
    rank: Rank
  ): Technique
  # update/deleteTechnique...
  createCombination(
    name: String 
    numTechniques: Int 
    maxRank: Rank 
    techniques: [TechniqueInput!]!
  ): Combination
}

The Mutation.js file includes this…

module.exports = {
createCombination: async (parent, args, ctx, info) => {
    
  const combination = await ctx.db.mutation.createCombination({
      data: {
        name: args.name,
        numTechniques: args.numTechniques,
        maxRank: args.maxRank,
        techniques: {
          // args.techniques is an array of objects like this { id: "prisma-id-string" }
          connect: [...args.techniques]
        }
      }
    }, info);

    return combination;
  },
}

This works well if the combination is something like

  • Jab
  • Cross
  • Kick

where the createCombination mutation would look something like

mutation {
  createCombination(
    name: "combination test"
    numTechniques: 4
    maxRank: NONE
    techniques: [
      {
        id: "jab-id"
      },
      {
        id: "cross-id"
      },
      {
        id: "kick-id"
      }
    ]
  )
  {
    name
    numTechniques
    techniques {
      name
     // returns jab, cross, kick
    }
  }
}

However it doesn’t work if I try multiple instances of the same technique within the combination (e.g. Jab, Jab, Jab or Jab, Cross, Jab, Cross).

mutation {
  createCombination(
    name: "combination 9"
    numTechniques: 4
    maxRank: NONE
    techniques: [
      {
        id: "jab-id"
      },
      {
        id: "jab-id"
      },
      {
        id: "jab-id"
      },
    ]
  )
  {
    name
    numTechniques
    techniques {
      name
      // returns jab only one time instead of three
    }
  }
}

In that case, the returned object includes only one of each of the multiple techniques. How do I ensure that the Combination object returned includes the same number of Techniques in the same order I started with? Thanks for any help you can offer!


#2

Your data model is flawed. You want multiple tecniques per combination in a specific order.
But your data model does not allow you to do this, all you can do is specify that certain combinations use certain tecniques. You store no information about the order or the amound of techniques per each combination in your database.

So what you want is to store the index of each technique as well as the technique itself in the combination.

Here is a suggestion for a datamodel that would allow you to do what you want:

type Technique {
  id: ID! @unique
  name: String! @unique
  description: String
  rank: Rank!
}

type Combination {
  id: ID! @unique
  name: String! @default(value: "Untitled")
  numTechniques: Int @default(value: "1")
  # make a connection to the CombinationTechnique type
  techniques: [CombinationTechnique!]!
  maxRank: Rank! @default(value: "NONE")
}

type CombinationTechnique {
  technique: Technique!
  index: Int!
}

#3

Thanks very much! Yes that makes sense. I’m having a little trouble getting my mutation to work correctly, but that’s probably a separate question if I can’t make progress with it.