Neo4j-graphql-js: Regression in v2.13.0

Created on 25 Mar 2020  路  10Comments  路  Source: neo4j-graphql/neo4j-graphql-js

After upgrading to v2.13.0, after running this query:

query KriyaCollectionDetailsQuery($id: ID!) {
  FeaturedKriyaCollection(id: $id) {
    id
    name
    backgroundColor
    imageUrl
    kriyaCount
    kriyas(orderBy: name_asc) {
      ...KriyaCardFragment
    }
  }
}

fragment KriyaCardFragment on Kriya {
  id
  name
  type
  maxTime
  minTime
  image {
    id
    url
  }
}

I get the following error every other time I run the query:

{
  "error": "Unexpected token < in JSON at position 1"
}

The query ran without error in v2.12.1.

I noticed that if I remove the fragment (and use the fragment's fields directly nested) as such, it works fine, and it executes significantly faster in both v2.12.1 and v2.13.0.

query KriyaCollectionDetailsQuery($id: ID!) {
  FeaturedKriyaCollection(id: $id) {
    id
    name
    backgroundColor
    imageUrl
    kriyaCount
    kriyas(orderBy: name_asc) {
      id
      name
      type
      maxTime
      minTime
      image {
        id
        url
      }
    }
  }
}

Here is the relevant schema:

type FeaturedKriyaCollection implements KriyaCollection {
  id: ID!
  name: String! @localize
  kriyas: [Kriya!]! @relation(name: "HAS_KRIYA", direction: "OUT")
  kriyaCount: Int!
    @cypher(statement: "MATCH (this)-[:HAS_KRIYA]->(kriya) RETURN count(kriya)")
  imageName: String
  imageUrl(options: ImageUrlInput): String @neo4j_ignore #calculated
  backgroundColor: String
}

type Kriya {
  id: ID!
  enabled: Boolean
  LOTId: ID
  firebaseId: ID
  name: String! @localize
  image: KriyaImage @relation(name: "USES_IMAGE", direction: "OUT")
  type: KriyaType!
  copyright: String
  defaultScaling: Float!
  maxTime: Int!
    @cypher(
      statement: "MATCH (this)-->(:Exercise)-->(p:ExercisePart) RETURN SUM(ROUND(p.maximum * COALESCE(p.averageTime, 1)))"
    )
  minTime: Int!
    @cypher(
      statement: "MATCH (this)-->(:Exercise)-->(p:ExercisePart) RETURN SUM(ROUND(p.minimum * COALESCE(p.averageTime, 1)))"
    )
  notes: String @localize #Markdown
  exercisesWithTuneInAndClose: [Exercise!]!
    @cypher(
      statement: "MATCH (e:Exercise { id: 'tune-in' }) RETURN e UNION WITH {this} AS this MATCH (this)-[r:HAS_EXERCISE]->(e:Exercise) RETURN e ORDER BY r.number UNION MATCH (e:Exercise { id: 'close' }) RETURN e"
    )
  exercises: [Exercise!]!
    @cypher(
      statement: "MATCH (this)-[r:HAS_EXERCISE]->(e:Exercise) RETURN e ORDER BY r.number"
    )
  exerciseCount: Int!
    @cypher(
      statement: "MATCH (this)-[r:HAS_EXERCISE]->(e:Exercise) RETURN count(e)"
    )
  exerciseRelations: [KriyaExerciseRelation!]!
  publications: [Publication!]! @relation(name: "HAS_KRIYA", direction: "IN")
  featuredKriyaCollections: [FeaturedKriyaCollection!]!
    @relation(name: "HAS_KRIYA", direction: "IN")
  concepts: [Concept!]! @relation(name: "RELATED_KRIYA", direction: "IN")
  activities: [KriyaPractice!]!
    @relation(name: "KRIYA_PRACTICED", direction: "IN")
  complete(userId: ID!): Boolean!
    @cypher(
      statement: """
      MATCH (this)
      RETURN exists((this)<-[:KRIYA_PRACTICED]-(:KriyaPractice)-[:BY_USER]->(user:User { id: $userId }))
      """
    )
  liked(userId: ID!): Boolean!
    @cypher(
      statement: """
      MATCH (this)
      RETURN exists((this)<-[:LIKES]-(:User { id: $userId }))
      """
    )
}

enum KriyaType {
  YogaSet
  Meditation
}

type KriyaExerciseRelation @relation(name: "HAS_EXERCISE") {
  from: Kriya!
  to: Exercise!
  number: Int!
}

Most helpful comment

Uh oh, I'll see if I can replicate this and take a look into it as soon as possible!

All 10 comments

Uh oh, I'll see if I can replicate this and take a look into it as soon as possible!

@michaeldgraham in the meantime, I wrote a simple Link for my client that expands fragment fields and removes the fragment definitions.

As I mentioned above, not using fragments seems to have sped up my query performance considerably. I haven't dug into the neo4j-graphql-js fragment implementation, but it seems something is slowing queries down when fragments are used.

We are also having some issues with upgrading to v2.13.0. With the combination of neo4j 4.0.2 and v2.13.0 we are getting the error in our tests when running the fileReport mutation:

const fileReportMutation = gql`
    mutation($resourceId: ID!, $reasonCategory: ReasonCategory!, $reasonDescription: String!) {
      fileReport(
        resourceId: $resourceId
        reasonCategory: $reasonCategory
        reasonDescription: $reasonDescription
      ) {
        createdAt
        reasonCategory
        reasonDescription
        reportId
        resource {
          __typename
          ... on User {
            name
          }
          ... on Post {
            title
          }
          ... on Comment {
            content
          }
        }
      }
    }
  `

results in this error:

"Abstract type ReportedResource must resolve to an Object type at runtime for field FiledReport.resource with value { __typename: \"Comment\", ... }, received \"undefined\". Either the ReportedResource type should provide a \"resolveType\" function or each possible type should provide an \"isTypeOf\" function."

I've found some other issues here, and looked at the docs linked to by you @johnymontana , but haven't worked it out. All the tests pass with v2.12.1 except that it throws the error Cannot read property 'name' of undefined.

The interesting thing about the fileReport mutation is that we do not use neo4j-graphql-js for it, but we do have a union for a return type:

type Report {
  id: ID!
  createdAt: String!
  updatedAt: String!
  rule: ReportRule!
  disable: Boolean!
  closed: Boolean!
  filed: [FILED]!
  reviewed: [REVIEWED]!
  resource: ReportedResource!
}

union ReportedResource = User | Post | Comment

and the Report is created properly, console.logging the return value looks fine, but this error :point_up_2: is thrown.

hope that helps. thanks for all your hard work!!

Uh oh, I'll see if I can replicate this and take a look into it as soon as possible!

@michaeldgraham any update on this?

Could you please test with v2.14.0 and let us know if you are seeing the same issues?

@johnymontana I just updated to v2.14.0, and I'm still getting errors without my FragmentFlatteningLink.

I'm not sure if the previous issue was fixed, but I'm now seeing a "Cannot return null for non-nullable field" error

@johnymontana after investigating this further, it appears the original query I posted when I opened this issue is running correctly, but I get the non-nullable field error with this query:

query {
  Webinar {
    id
    ...VideoCardFragment
  }
}

fragment VideoCardFragment on Video {
  id
  fileUrl
}

here are the relevant schema bits:

type Webinar implements Video {
  id: ID!
  name: String! @localize
  awsInputId: ID! # The id of the original input video on AWS
  awsOutputId: ID! # The id of the output video on AWS
  fileUrl: String! @neo4j_ignore #calculated
  imageUrl: String! @neo4j_ignore #calculated
  duration: Int! # seconds
  views: [VideoView!]! @relation(name: "VIDEO_VIEWED", direction: "IN")

  createdAt: _Neo4jDateTime!
  writeup: String @localize # Markdown
}

interface Video {
  id: ID!
  name: String! @localize
  awsInputId: ID! # The id of the original input video on AWS
  awsOutputId: ID! # The id of the output video on AWS
  fileUrl: String! @neo4j_ignore #calculated
  imageUrl: String! @neo4j_ignore #calculated
  duration: Int! # seconds
  views: [VideoView!]! @relation(name: "VIDEO_VIEWED", direction: "IN")
}

Maybe it has something to do with the fact that fileUrl uses a custom resolver? I tried replacing fileUrl with another field and still get the error. I've also confirmed that the query DOES run correctly if I just replace the fragment with the actual fields.

@smkhalsa Hello, would you consider making your FragmentFlatteningLink public?

@benjamin-rood Here it is. It's written in Dart, so you may need to adapt it.

https://gist.github.com/smkhalsa/a7c2270790b36a76729c03ec52888d06

@smkhalsa if you change your fragment declaration to be ... on Webinar instead of video does that query work?

query {
  Webinar {
    id
    ...VideoCardFragment
  }
}

fragment VideoCardFragment on Webinar {
  id
  fileUrl
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

vijayst picture vijayst  路  5Comments

benpetsch picture benpetsch  路  3Comments

purplemana picture purplemana  路  3Comments

NathanPimlott picture NathanPimlott  路  4Comments

bebbi picture bebbi  路  6Comments