Skip to content

Danger when querying interfaces #113

Closed
@Arnarkari93

Description

@Arnarkari93

I spotted a dangerous query that reason-relay generated types could prevent. I made a PR here https://github.com/sothebys/reason-relay/pull/1/files with the changes that result in the type system not giving errors but resulting in runtime errors.

Consider the following query

module Query = [%relay.query
  {|
    query TestNodeInterfaceQuery {
      node(id: "123") {
        __typename
        id
        ... on User {
          firstName
        }
        ... on Group {
          name
        }
      }
    }
|}
];

Then generating the types for this query reason-relay will not to create a variant type that you can switch over for User and Group instead a record will be generated with all properties marked as optional. This is because the field id id outside of the interface selections.

// ___generated___/TestNodeInterfaceQuery.re
type response = {
  id: String,
  firstName: option(String),
  name: option(String),
};

This can seem harmless at first but lets say if we add a fragment to get the user's lastname to the User like so:

module Query = [%relay.query
  {|
    query TestNodeInterfaceQuery {
      node(id: "123") {
        __typename
        id
        ... on User {
          firstName
          ... TestNodeInterface_user
        }
        ... on Group {
          name
        }
      }
    }
|}
];

  module Fragment = [%relay.fragment
    {|
    fragment TestNodeInterface_user on User {
      lastName
    }
  |}
  ];

We will get the property fragmentRefs as expected added to the type response, but If we try to pass the fragmentRefs to a component that uses the lastName property we will get runtime error if the query response is indeed a Group since lastName is not a valid proprty on the type Group.

Whould it make sense to generate the types so that variants are always generated?
So the same example as above would generate following types:

type response_user = {
  firstName: String
  fragmentRefs: [|` TestNodeInterface_user]
};
type response_group = {
  name: String
};
type response = {
  id: String
  _type: [ |`User(response_user) | `Group(response_group)]
};

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions