Relationships

The Quickstart example used a very simple model with only one type, containing one property. The Types section explored configuration file format and the resulting GraphQL schema in more detail. However, Warpgrapher can generate create, read, update, and delete operations for relationships between types as well. The configuration below includes describes two types and a relationship between them.

version: 1
model:
  - name: User
    props:
      - name: email
        type: String
        required: false
  - name: Organization
    props:
      - name: name
        type: String
        required: false
    rels:
      - name: members
        nodes: [User]
        list: true
        props:
          - name: joinDate
            type: String
            required: false

The configuration above adds a second type, called Organization. The definition of the organization type contains the rels attribute, which was not seen in the earlier example. The rels attribute contains a list of permissible relationships between nodes. In this case, the configuration adds a members relationship from nodes of of the Organization type to nodes of the User type, indicating that certain users are members of an organization. The name attribute in the configuration is the name of the relationship and must be unique within the scope of that type. The nodes attribute is a list of other types that may be at the destination end of the relationship. In this case, the only type at may be a member is the User type, but in other use cases, the destination node might be allowed to be one of several types. Lastly, the list attribute is true, indicating that an Organization may have more than one member.

Relationship Configuration

The example configuration above is fairly simple, and does not make use of several optional attributes. The definition below shows the full set of configuration options that are permissible on a relationship.

model:
  - name: String
    rels:
      - name: String
        nodes: [String]  # Values in the list must be other types in the model
        list: Boolean
        props:
          - name: String
            uses:
              create: Boolean
              query: Boolean
              update: Boolean
              output: Boolean
            type: String  # Boolean | Float | ID | Int | String
            required: Boolean
            list: Boolean
            resolver: String
            validator: String
        endpoints:
          read: Boolean
          create: Boolean
          update: Boolean
          delete: Boolean
        resolver: String

The snippet above shows that relationships are defined in a list under the rels attribute within a type definition. Each relationship has a name that must be unique within the scope of that type. The nodes attribute is a list of name of types within the model that can appear as destination nodes in the relationship. Note that the a type may appear in its own relationship's nodes lists. A node is permitted to have relationships to nodes of the same type.

If the list attribute is true, then a node may have relationships of the same type to multiple destination nodes, modeling one-to-many relationships. If list is false, then the node may only have a single relationship of that type, to a single destination node.

The props attribute on a relationship works the same way that the props attribute works on nodes, except that the properties are associated with the relationship rather than with the node. See the description of the props attribute in the section on types for more details.

Similarly, the endpoints attribute on relationships works the same way that it does on nodes. The individual boolean attributes within the endpoints object control whether Warpgrapher generates GraphQL schema elements for create, read, update, and delete operations. Just as with types, the default for all the boolean values is true, meaning that by default Warpgrapher creates schema elements and resolvers for all CRUD operations.

Lastly, the resolver attribute is also similar to the attribute of the same name on property definitions. The string in the resolver attribute is mapped to a custom-written Rust function provided when setting up the Warpgrapher engine. This allows systems using Warpgrapher to control the behavior of resolving some relationships. Use cases for this include dynamically-generated relationships that are computed at query time rather than being stored in the back-end data store.

Generated Schema

This section describes each of the GraphQL schema elements that Warpgrapher generates for CRUD operations on relationships. Discussion of the schema elements related solely to types, absent relationships, was covered previously in the types section.

Queries in a Model with Relationships

The top level GraphQL query object includes three (3) queries. This should make intuitive sense. The model has two nodes, Organization and User, and one relationship, the OrganizationMembers relationship from a source organization to a destination user that is a member of that organization. Warpgrapher's generated schema allows for querying either node type or the relationship between them. As will be discussed in detail below, the inputs to these query operations have a recursive structure, so that using the top level query for the relationship, it is possible to filter based on the properties of the source or destination nodes. Similarly, when querying for a node type, it is possible to add search parameters related to relationships, the destinations of those relationships, and so on.

type Query {
  Organization(
    input: OrganizationQueryInput,
    options: OrganizationOptions
  ): [Organization!]

  OrganizationMembers(
    input: OrganizationMembersQueryInput,
    options: OrganizationMembersOptions
  ): [OrganizationMembersRel!]

  User(input: UserQueryInput, options: UserOptions): [User!]

  _version: String
}

Querying for a Relationship

In the top level GraphQL query, note that a new query, called OrganizationMembers has been generated for the relationship. This query has an input parameter, OrganizationMembersQueryInput that provides search query arguments to select the set of relationships to be returned.

The OrganizationMembersQueryInput query parameter, defined below, provides a means to search for a given instance of a relationship. It is possible to search based on an id or set of IDs, and the joinDate attribute allows queries based on the properties on the relationship. In addition to using the id or another property on the relationship, the OrganizationMembersQueryInput parameter also includes a src and a dst attribute. These attributes allow Warpgrapher clients to search for relationships based on properties of the source or destination nodes joined by the relationship.

input OrganizationMembersQueryInput {
  dst: OrganizationMembersDstQueryInput
  id: StringQueryInput
  joinDate: StringQueryInput
  src: OrganizationMembersSrcQueryInput
}

The two input objects for the src and dst input objects are shown below. Note that for the source query input, the only attribute is an Organization attribute that is an OrganizationQueryInput and that for the destination, the only attribute is a User attribute that is a UserQueryInput. There are two important observations here. First, the reason for having the OrganizationMembersDstQueryInput object is that a relationship might have more than one node type as a possible destination. When building the GraphQL schema, Warpgrapher has to allow for the client to query any of those possible destination nodes. In this example, the only type of destination node is a User, so that's the only possibility shown below. If the nodes list had more types of nodes, any of those node types could be queried through the OrganizationMembersDstQueryInput. The second observation is that both the OrganizationQueryInput and the UserQueryInput inputs are the same input parameters used to query for a set of nodes in the Organization and User root level GraphQL queries shown above.

input OrganizationMembersSrcQueryInput {
  Organization: OrganizationQueryInput
}

input OrganizationMembersDstQueryInput {
  User: UserQueryInput
}

We'll come back to the node-based query input in a moment, in the section below on Querying for a Node. First, the code snippet below shows the schema for output from the relationship query. The relationship includes four attributes, a unique identifier for the relationship called id, joinDate for the property configured on the relationship, and src and dst attributes that represent the source and destination nodes respectively.

type OrganizationMembersRel {
  dst: OrganizationMembersNodesUnion!
  id: ID!
  joinDate: String
  src: Organization!
}

The src attribute in the OrganizationMembersRel output type is an Organization type, which is exactly the same output type used for node queries, and so will be covered in the section on querying for nodes, below. The dst attribute is a little more complex. Recall from the description of the configuration schema that Warpgrapher may connect from a source node type to a destination that can be one of many node types. A GraphQL union type is used to represent the multiple destination node types that may exist. As shown in the schema snippet below, in this example of OrganizationMembersNodesUnion, there is only a single destination node type of User. A more complex configuration might have multiple node types in the union.

union OrganizationMembersNodesUnion = User

Note that the User type is the same type that is used to return users in queries for nodes.

The options argument, described above as an argument for the OrganizationMembers query as a whole, is of type OrganizationMembersOptions. The OrganizationMembersOptions type has a single property, called sort, which is a list of zero or more OrganizationMembersSort objects. Each OrganizationMembersSort object has two enumeration properties, direction and orderBy.

type OrganizationMembersOptions {
  sort: [OrganizationMembersSort!]
}

type UserSort {
  direction: DirectionEnum
  orderBy: OrganizationMembersOrderByEnum!
}

enum DirectionEnum {
  ascending
  descending
}

enum OrganizationMembersOrderByEnum {
  id
  dst:email
}

The OrganizationMembersOrderByEnum has variant values for each of the properties (but not relationships) on the OrganizationMembers relationship, though in this case that's only the id property. Additionally, the enum has variants for each of the properties on the destination object, allowing the results to be sorted either by properties on the relationship itself, or those on the destination object. By including one or more values in the sort array provided to UserOptions, it is possible to sort results coming back from Warpgrapher. The direction property determines whether the results are returned in ascending or descending sort order. The orderBy field determines on which property the results are sorted. For example, above, an orderBy field with a value of dst:email would sort the organization member's relationship results in alphabetical order of member email addresses. If the sort array contains more than one value, then resorts groups of results with the same first sort key are further sorted by the second key, and so on. For example, a sort array might have entries for joinDate and then name to sort first by the date someone joined, and alphabetically for all people who joined on the same date.

Querying for a Node

The root GraphQL Query object has queries for each of the node types in the configuration. To see how relationships affect node queries, have a look at the Organization query, beginning with the OrganizationQueryInput definition in the snippet below. In addition to the id and name attributes for searching based on the scalar properties of the type, the schema also includes a members attribute, of type OrganizationMembersQueryInput. This is the same input object described above that's used in the root level query for the OrganizationMembers relationship. This recursive schema structure is really quite powerful, as it allows the client to query for nodes based on a combination of the node's property values, the values of properties in the relationships that it has, and the values of properties in the destination nodes at the other end of those relationships, to any level of depth. For example, it would be easy to construct a query that retrieves all of the organizations that contain a particular user as a member. For examples of relationship-based queries, see the chapter on API usage.

input OrganizationQueryInput {
  id: StringQueryInput
  members: OrganizationMembersQueryInput
  name: StringQueryInput
}

Relationshps information can be navigated in the output type for the node, as well. The Organization output type shown in the snippet below includes both the scalar properties on the type, the id and name, as well as the relationship to the members of the Organization. The members attribute includes an input of type OrganizationMembersQueryInput. This is the same input type that is used to query for members relationships from the GraphQL root query, desribed above. This means that when retrieving Organization nodes, it's possible to filter the set of members that you want to retrieve in a nested query. Again, the recursive structure of the schema generated by Warpgrapher allows you the flexibility to query to any level of depth in a sub-graph that is needed.

type Organization {
  id: ID!
  members(input: OrganizationMembersQueryInput): [OrganizationMembersRel!]
  name: String
}

Mutations in a Model with Relationships

The GraphQL schema's top level mutation object contains nine (9) mutations. This should make intuitive sense. There are three mutations (create, update, and delete), and three kinds of things that can be mutated: organization nodes, user nodes, and membership relationships between organizations and nodes. There are quite a few nested input and output types contributing to these mutations. The high-level principle to keep in mind is that Warpgrapher allows recursive operations that support manipulation of whole sub-graphs at a time. For example, node mutations have nested input objects that allow manipulation of the relationships on those nodes, and the destination nodes at the end of those relationships, and so on.

type Mutation {
  OrganizationCreate(
    input: OrganizationCreateMutationInput!
    options: OrganizationOptions
  ): Organization
  OrganizationDelete(input: OrganizationDeleteInput!, options: OrganizationOptions): Int
  OrganizationMembersCreate(
    input: OrganizationMembersCreateInput!
    options: OrganizationMembersOptions
  ): [OrganizationMembersRel!]
  OrganizationMembersDelete(
    input: OrganizationMembersDeleteInput!
    options: OrganizationMembersOptions
  ): Int
  OrganizationMembersUpdate(
    input: OrganizationMembersUpdateInput!
    options: OrganizationMembersOptions
  ): [OrganizationMembersRel!]
  OrganizationUpdate(
    input: OrganizationUpdateInput!
    options: OrganizationOptions
  ): [Organization!]
  UserCreate(input: UserCreateMutationInput!, options: UserOptions): User
  UserDelete(input: UserDeleteInput!, options: UserOptions): Int
  UserUpdate(input: UserUpdateInput!, options: UserOptions): [User!]
}

Mutating a Relationship

Creating a Relationship

The snippet below contains the input for creation of one or more OrganizationMembers relationships. There are two attributes, MATCH and CREATE. The MATCH attribute is used to identify the organization or organizations that should be matched as the source of the relationship(s) to be created. It has the same type, OrganizationQueryInput that is used to query for nodes using the Organization query under the GraphQL Query root described above. The match query may select more than one node, allowing similar relationships to be created in bulk. Matching existing source nodes is the only option when creating a relationship. If it is necessary to create the node at the source end of the relationship, see the node creation operation, in this case OrganizationCreate instead.

input OrganizationMembersCreateInput {
  CREATE: [OrganizationMembersCreateMutationInput!]
  MATCH: OrganizationQueryInput
}

The CREATE attribute has a type of OrganizationMembersCreateMutationInput. That input structure is shown in the schema snippet below. It includes the joinDate attribute on the relationship. The id object is accepted as an input to facilitate offline operation, in which the client may need to choose the unique identifier for the relationship. If the client does not choose the identifier, it will be randomly assigned by the Warpgrapher service.

input OrganizationMembersCreateMutationInput {
  dst: OrganizationMembersNodesMutationInputUnion!
  id: ID
  joinDate: String
}

The dst property in the OrganizationMembersCreateMutationInput above is of type OrganizationMembersNodesMutationInputUnion, which is included in the schema snippet below. Don't be intimidated by the lengthy name of the union type. Recall that in the configuration above, the destination type of a relationship is allowed to have more than one type. In this configuration, it only has one type, but the OrganizationMembersNodesMutationInputUnion is what allows the destination of the relationship to have multiple types. In this case, the only option is User, with a type of UserInput.

input OrganizationMembersNodesMutationInputUnion {
  User: UserInput
}

The UserInput type, which provides the destination node for the relationship(s) to be created, has two attributes. When using the EXISTING attribute, Warpgrapher search the graph database for a set of nodes matching the UserQueryInput search criteria and uses the results as the destination nodes for creation of the relationship(s). Note that this UserQueryInput type is the same input type that is used to query for users in the user query under the GraphQL root Query. No matter where in the recursive hierarchy, searhing for User nodes always uses the same input. The NEW attribute creates a new User node as the destination of the relationship. Note that the UserCreateMutationInput input type is the same input type used to create a User node in the UserCreate mutation under the GraphQL root Mutation object.

input UserInput {
  EXISTING: UserQueryInput
  NEW: UserCreateMutationInput
}

The output of creating one or more relationships, OrganizationMembersRel, is the same output type returned from querying for the organization's members relationship, as was described in the section on queries, above. It contains the newly created relationship.

Updating a Relationship

The input for a relationship update mutation, OrganizationMembersUpdateInput is shown in the schema snippet below. The update input consists of two parts. The MATCH attribute is a query input to identify the relationships that should be updated. Note that the match input type, OrganizationMembersQueryInput is the same input type used to provide search parameters when searching for relationships under the OrganizationMembers query under the GraphQL root Query object. The SET attribute is used to describe the changes that should be made to values in the relationship(s) matched by the MATCH parameter, and potentially the sub-graph beneath.

input OrganizationMembersUpdateInput {
  MATCH: OrganizationMembersQueryInput
  SET: OrganizationMembersUpdateMutationInput!
}

The SET input is of type OrganizationMembersUpdateMutationInput, shown in the snippet below. The joinDate attribute is the same input type used during relationship creation operations, described in the section above. The src and dst attributes allow a single update to provide new values not only for the relationship properties, but also properties on the source and destination nodes at the ends of the relationship.

input OrganizationMembersUpdateMutationInput {
  dst: OrganizationMembersDstUpdateMutationInput
  joinDate: String
  src: OrganizationMembersSrcUpdateMutationInput
}

The source and destination node input types are shown in the schema snippets below. Note that the types, OrganizationUpdateMutationInput and UserUpdateMutationInput are the same input types used for the SET attributes in the single node update operation, described in in the section on single-node mutation operations below. Thus, we have hit the point where the GraphQL schema structure that Warpgrapher generates is recursive. A relationship update mutation can update the properties on the relationship, as described just above, or using this recursive input structure, reach down into the source and destination nodes at the ends of the relationship and edit their properties as well.

input OrganizationMembersSrcUpdateMutationInput {
  Organization: OrganizationUpdateMutationInput
}

input OrganizationMembersDstUpdateMutationInput {
  User: UserUpdateMutationInput
}

The output for updating one or more relationships, OrganizationMembersRel, is the same output type returned from querying for an organization's members relationship, as was described in the section on queries, above. For update operations, it returns the list of relationships that were updated in the mutation.

Deleting a Relationship

The input for a relationship delete mutation, OrganizationMembersDeleteInput, is shown in the schema snippet below. The MATCH attribute is used to query for the relationships that are desired to be deleted. Note that the input type, OrganizationMembersQueryInput is the same input type used to query for relationships under the relationship query in the GraphQL root Query object, described in the section on querying, above.

input OrganizationMembersDeleteInput {
  MATCH: OrganizationMembersQueryInput
  dst: OrganizationMembersDstDeleteMutationInput
  src: OrganizationMembersSrcDeleteMutationInput
}

The src and destination delete mutation inputs are not particularly interesting for this simple schema. The input type for the src of the relationship contains a single Organization attribute that has the same type as the deletion input for an OrganizationDelete mutation. However, the only option in that type is deletion of members, which is what is already being done. On the destination side, because the User type has no relationships of its own, the UserDeleteMutationInput object is empty altogether. Thus, for the most part, the src and dst attriubtes on the OrganizationMembersDeleteInput are not particularly useful, though in more complex models, they allows the possibility of deleting multiple nodes and relationships in a single query.

input OrganizationMembersSrcDeleteMutationInput {
  Organization: OrganizationDeleteMutationInput
}

input OrganizationMembersDstDeleteMutationInput {
  User: UserDeleteMutationInput
}

input OrganizationDeleteMutationInput {
  members: [OrganizationMembersDeleteInput!]
}

input UserDeleteMutationInput

The output from the relationship deletion mutation is an integer with a count of the relationships deleted.

Mutating a Node

In many ways, modifying a node in a data model that includes relationships is similar to what was described in the node-only portion of the book, previously. Thus, this section doesn't repeat that same content, instead focusing only on the changes the come from having a relationship in the mix.

Creating a Node

The snippet below contains the input for creation of an organization. Note the members attribute, of type OrganizationMembersCreateMutationInput, which allows for the creation of members attributes in the same mutation that creates the organization. The OrganizationMembersCreateMutationInput input type is the same one that is used for the CREATE attribute in the OrganizationMembersCreate mutation under the root GraphQL mutation object. Thus, when creating a node, you can create members for it using the same full flexbility provided by the mutation dedicated to creating relationships. The recursive nature of the creation inputs allows for the creation of entire sub-graphs.

input OrganizationCreateMutationInput {
  id: ID
  members: [OrganizationMembersCreateMutationInput!]
  name: String
}

The rest of the inputs and output for the node creation mutation are the same as those described previously for a simpler model without relationships.

Updating a Node

The OrganizationUpdateInput for making changes to organizations looks similar to the input types used for objects that don't have relationships. It has a MATCH attribute to select the objects to update, and a SET attribute to describe the changes to be made. The difference is in the

input OrganizationUpdateInput {
  MATCH: OrganizationQueryInput
  SET: OrganizationUpdateMutationInput
}

input OrganizationUpdateMutationInput {
  members: [OrganizationMembersChangeInput!]
  name: String
}

The differences for the inclusion of relationships begin in the OrganizationUpdateMutationInput input type used to set new values for the nodes to be updated, which includes a members attribute of type OrganizationMembersChangeInput. There are three changes one could make to a relationship: add one or more new relationships to destination nodes, delete one or more relationships to destination nodes, or keep the relationships to the same set of destination nodes but make changes to the properties of one or more of those destination nodes. Those options are captured in the OrganizationMembersChangeInput input type in the schema snippet below.

input OrganizationMembersChangeInput {
  ADD: OrganizationMembersCreateMutationInput
  DELETE: OrganizationMembersDeleteInput
  UPDATE: OrganizationMembersUpdateInput
}

The OrganizationMembersCreateMutationInput input type for the ADD operation is the same one that was described above as the CREATE attribute the section on mutations to create new relationships. This makes sense, as in this context it is already clear what the source node or nodes are, and the ADD attribute need only create the new relationships to be added. Similarly, the OrganizationMembersDeleteInput used for the DELETE attribute here is the same one that is used for the OrganizationMembersDelete operation under the root GraphQL Mutation type. The match will be scoped to the relationships under the source node(s) selected by the OrganizationUpdateInput MATCH query. As expected, the same is true for the OrganizationMembersUpdateInput input type used for the UPDATE attribute. It's the same as the input used for the OrganizationMembersUpdate mutation under the root GraphQL Mutation type.

Deleting a Node

The OrganizationDeleteInput input type, shown in the schema snippet below, looks similar to the one for nodes without relationships. However, the OrganizationDeleteMutationInput is different, as it includes a members attribute of type OrganizationMembersDeleteInput, which is the same type used for the OrganizationMembersDelete mutation under the GraphQL root Mutation type. In the case of this model, this additional input does little. In a more complex model with multiple types of relationships, however, it would allow for deletion of whole subgraphs of nodes and relationships.

input OrganizationDeleteInput {
  DELETE: OrganizationDeleteMutationInput
  MATCH: OrganizationQueryInput
}

input OrganizationDeleteMutationInput {
  members: [OrganizationMembersDeleteInput!]
}

Full Schema Listing

The full schema for the example above is included below.

input OrganizationMembersDeleteInput {
  MATCH: OrganizationMembersQueryInput
  dst: OrganizationMembersDstDeleteMutationInput
  src: OrganizationMembersSrcDeleteMutationInput
}

input OrganizationCreateMutationInput {
  id: ID
  members: [OrganizationMembersCreateMutationInput!]
  name: String
}

input OrganizationMembersCreateInput {
  CREATE: [OrganizationMembersCreateMutationInput!]
  MATCH: OrganizationQueryInput
}

input OrganizationMembersSrcQueryInput {
  Organization: OrganizationQueryInput
}

type Mutation {
  OrganizationCreate(
    input: OrganizationCreateMutationInput!
    options: OrganizationOptions
  ): Organization
  OrganizationDelete(input: OrganizationDeleteInput!, options: OrganizationOptions): Int
  OrganizationMembersCreate(
    input: OrganizationMembersCreateInput!
    options: OrganizationMembersOptions
  ): [OrganizationMembersRel!]
  OrganizationMembersDelete(
    input: OrganizationMembersDeleteInput!
    options: OrganizationMembersOptions
  ): Int
  OrganizationMembersUpdate(
    input: OrganizationMembersUpdateInput!
    options: OrganizationMembersOptions
  ): [OrganizationMembersRel!]
  OrganizationUpdate(
    input: OrganizationUpdateInput!
    options: OrganizationOptions
  ): [Organization!]
  UserCreate(input: UserCreateMutationInput!, options: UserOptions): User
  UserDelete(input: UserDeleteInput!, options: UserOptions): Int
  UserUpdate(input: UserUpdateInput!, options: UserOptions): [User!]
}

input OrganizationMembersChangeInput {
  ADD: OrganizationMembersCreateMutationInput
  DELETE: OrganizationMembersDeleteInput
  UPDATE: OrganizationMembersUpdateInput
}

input UserUpdateMutationInput {
  email: String
}

input UserDeleteInput {
  DELETE: UserDeleteMutationInput
  MATCH: UserQueryInput
}

input OrganizationMembersNodesMutationInputUnion {
  User: UserInput
}

input UserInput {
  EXISTING: UserQueryInput
  NEW: UserCreateMutationInput
}

input OrganizationQueryInput {
  id: StringQueryInput
  members: OrganizationMembersQueryInput
  name: StringQueryInput
}

union OrganizationMembersNodesUnion = User

type Query {
  Organization(
    input: OrganizationQueryInput
    options: OrganizationOptions
  ): [Organization!]
  OrganizationMembers(
    input: OrganizationMembersQueryInput
    options: OrganizationOptions
  ): [OrganizationMembersRel!]
  User(input: UserQueryInput, options: UserOptions): [User!]
  _version: String
}

input OrganizationMembersDstDeleteMutationInput {
  User: UserDeleteMutationInput
}

input OrganizationMembersUpdateInput {
  MATCH: OrganizationMembersQueryInput
  SET: OrganizationMembersUpdateMutationInput!
}

input OrganizationMembersSrcUpdateMutationInput {
  Organization: OrganizationUpdateMutationInput
}

input UserUpdateInput {
  MATCH: UserQueryInput
  SET: UserUpdateMutationInput
}

input OrganizationUpdateInput {
  MATCH: OrganizationQueryInput
  SET: OrganizationUpdateMutationInput
}

type OrganizationMembersRel {
  dst: OrganizationMembersNodesUnion!
  id: ID!
  joinDate: String
  src: Organization!
}

input OrganizationMembersUpdateMutationInput {
  dst: OrganizationMembersDstUpdateMutationInput
  joinDate: String
  src: OrganizationMembersSrcUpdateMutationInput
}

input OrganizationMembersSrcDeleteMutationInput {
  Organization: OrganizationDeleteMutationInput
}

input UserQueryInput {
  email: StringQueryInput
  id: StringQueryInput
}

input OrganizationMembersQueryInput {
  dst: OrganizationMembersDstQueryInput
  id: StringQueryInput
  joinDate: StringQueryInput
  src: OrganizationMembersSrcQueryInput
}

input OrganizationDeleteMutationInput {
  members: [OrganizationMembersDeleteInput!]
}

type Organization {
  id: ID!
  members(input: OrganizationMembersQueryInput): [OrganizationMembersRel!]
  name: String
}

input OrganizationUpdateMutationInput {
  members: [OrganizationMembersChangeInput!]
  name: String
}

type Subscription

input OrganizationMembersCreateMutationInput {
  dst: OrganizationMembersNodesMutationInputUnion!
  id: ID
  joinDate: String
}

input UserDeleteMutationInput

input OrganizationMembersDstUpdateMutationInput {
  User: UserUpdateMutationInput
}

type User {
  email: String
  id: ID!
}

input OrganizationMembersDstQueryInput {
  User: UserQueryInput
}

input UserCreateMutationInput {
  email: String
  id: ID
}

input StringQueryInput {
  CONTAINS: String
  EQ: String
  GT: String
  GTE: String
  IN: [String!]
  LT: String
  LTE: String
  NOTCONTAINS: String
  NOTEQ: String
  NOTIN: [String!]
}

input OrganizationDeleteInput {
  DELETE: OrganizationDeleteMutationInput
  MATCH: OrganizationQueryInput
}