GraphQL mutation isn't supporting interface

Hello, I am having a bit of trouble relating a new record to a parent node which is defined via interface.

Here’s the setup:

interface Production {
    scenes: [Scene] @hasInverse(field: production)
}

type Episode implements Production {
    title: String!
}

type Film implements Production {
    title: String!
}

type Scene {
    sceneNumber: Int!
    production: Production!
}

Now when trying to create a new scene the “production” field is not present in the AddSceneInput generated type, which prevents me from creating a new Scene.

Can someone point out what I’m doing incorrectly?

Thanks!

Hey @DaRizat, to create a scene you would first need to have one production as you would be linking the scene to production.

First of all, you would need to add ID or @id field to the Production interface. So, modifying it something like this this should work.

interface Production {
    id: ID!
    scenes: [Scene] @hasInverse(field: production)
}

Then add any production,

mutation addFilm($film: AddFilmInput!){
  addFilm(input: [$film]){
    film {
      title
      id
    }
  }
}

with payload say,

{
	"film": {
		"title": "3 idiots"
	}
}

Then you may add a scene,

mutation AddScene($scene: AddSceneInput!) {
  addScene(input: [$scene]) {
    scene {
      sceneNumber
      production {
        id
      }
    }
  }
}

with payload

{
	"scene": {
		"sceneNumber": 1,
		"production": {
			"id": "0x2712"
		}
	}
}

where 0x2712 is an id for the film “3 idiots”.

moving this to Users/GraphQL

Hey Naman,

Thank you for the response. I simplified the schema a bit which probably led to your diagnosis, my apologies. Film and Episode both have ID individually defined either directly or through other implementations. It’s actually like this

type Account {
  id: ID!
  name: String! @search(by: [term])
  projects: [Project] @hasInverse(field: account)
}

interface Project {
  id: ID!
  account: Account!
}

interface Production {
  scenes: [Scene] @hasInverse(field: production)
}

type Series implements Project {
  episodes: [Episode] @hasInverse(field: series)
}

type Film implements Project & Production

type Episode implements Production {
  id: ID!
  title: String
  episodeNumber: String
}

Having Film implementing Project is causing me to be unable to define the ID on the Production interface. I’m used to doing things like this with union types which I know aren’t supported. I also understand I could add some containership at a level higher than Film that would also alleviate this problem but I was wondering if you had a quick fix for a situation like this where many interfaces could potentially be shared? Trying to reduce UI Logic if at all possible.

Hey Jack,

One workaround to get things working with minimal changes would be to have a field with @id directive in the Production. Adding a temp field that makes the node unique should help. Please see if this works for you.

interface Production {
  temp: String! @id
  scenes: [Scene] @hasInverse(field: production)
}

Tagging @abhimanyusinghgaur, maybe he can provide you some better alternative.

At present, to go on with your schema, you could do something like this:

type Account {
  id: ID!
  name: String! @search(by: [term])
  projects: [Project] @hasInverse(field: account)
}

interface Project {
  account: Account!
}

interface Production {
  id: ID!
  scenes: [Scene] @hasInverse(field: production)
}

type Series implements Project {
  id: ID!
  episodes: [Episode] @hasInverse(field: series)
}

type Film implements Project & Production

type Episode implements Production {
  title: String
  episodeNumber: String
  series: Series
}

type Scene {
  id: ID!
  sceneNumber: Int!
  production: Production!
}

Notice that I have removed the ID field from Project and added it to Production, also, added ID field to the types implementing Project but not Production.

This feels like avoiding the obstacles as long as you can, to make things work. But, doesn’t seem like a good approach in the long run as you may have a lot of types implementing Project and then you may really want to have an ID field in Project.

The problem starts with Film implementing both Project and Production, and let’s say if we had an ID field in both Project and Production, then currently it would give an error because type Film can’t have two ID fields. So, what I thought of as a solution to this problem is a directive like @inheritID, as shown in the below example. So, consider these two interfaces:

interface Project {
  projectID: ID!
  ...
}

interface Production {
  productionID: ID!
  ...
}

Now, you could either inherit the ID field from one of the implementing types, like this:

type Film implements Project & Production @inheritID(inherit: true, from: "Production")

OR, you could choose to define your own ID field in Film, like this:

type Film implements Project & Production @inheritID(inherit: false) {
  id: ID!
}

But, this directive itself may cause some problems with hasInverse usage. I haven’t given a thought to that yet. I guess we need to think about this case a bit more to find a nice solution.

1 Like