Reindex
TeamBlogDocs

Mutations

Reindex mutations allow you to perform basic create, update, delete operations on nodes and also add and remove nodes in the many-to-many relationships.

All Reindex mutations share several common traits - they all have one argument called input, that holds one optional field clientMutationId. Mutations return a payload object, which also always contains a clientMutationId.

If clientMutationId is passed to input it will always be returned as-is in the payload. This way client libraries, like Relay, can tell results of different mutations apart.

Create, Update, Replace, Delete

Those basic operations share payloads and have similar input formats. They accept all fields that can be modified in the node. The difference is that update and replace also require id of the node being modified in the input, while create doesn’t allow it. delete just accepts the id of the type in the input.

Payload types are generated from the types being modified by the mutation. Their structure is the same for all the mutations of that type.

type _XPayload {
  clientMutationId: String
  id: ID
  changedX: X
  changedXEdge: _XEdge

  viewer: ReindexViewer
  # Fields with Nodes to which Node fields of the type point
  y: Y,
  # for example
  user: User,
}

As payloads need to support various mutation types of Relay, there are multiple fields included in them. In addition to clientMutationId, there is:

  • id - the ID of the mutated node

  • changedX - the mutated node

  • changedXEdge - a connection edge containg the mutated node. Can be used to add a newly created node to a connection in Relay.

  • viewer - The global viewer object. Can be used on the client to add a newly created node to the connection of all nodes of the type.

    E.g. when creating a Todo object, you can add it to viewer.allTodos using the following Relay mutation config:

    {
      type: 'RANGE_ADD',
      parentID: this.props.viewer.id,
      connectionName: 'allTodos',
      edgeName: 'changedTodoEdge',
      rangeBehaviors: {
        '': 'prepend',
      },
    }
    
  • All the node reference fields in this type. These can be used to refer to the parent node for RANGE_ADD mutations in Relay.

See this guide for more information about Relay mutations.

createX

mutation createX(input: _CreateXInput): _XPayload

Create a node of some type. input is a flat object with all the fields of the type alongside clientMutationId. All nonNull fields are required.

mutation {
  createUser(input: {
    username: "freiksenet",
    email: "freiksenet@example.com"
  }) {
    changedUser {
      id
      username
      email
    }
  }
}

updateX

mutation updateX(input: _UpdateXInput): _XPayload

Update the given node. Given fields are merged into existing object. input is a flat object with all the fields of the type alongside clientMutationId and id. id is an id of the node being updated and it is required.

mutation {
  updateUser(input: {
    id: "some-user-id"
    username: "freiksenets-new-name",
  }) {
    changedUser {
      id
      username
    }
  }
}

replaceX

mutation replaceX(input: _ReplaceXInput): _XPayload

Replaces the given node with a new one. input is a flat object with all the fields of the type alongside clientMutationId and id. id is an id of the node being replaced and it is required. All nonNull fields are required.

mutation {
  replaceUser(input: {
    id: "some-user-id"
    username: "freiksenets-new-name",
    email: "freiksenet-new@example.com"
  }) {
    changedUser {
      id
      username
      email
    }
  }
}

deleteX

mutation deleteX(input: _DeleteXInput): _XPayload

Delete the given node. input is an object with fields clientMutationId and id. id is an id of the node being deleted and it is required.

mutation {
  deleteUser(input: {
    id: "some-user-id"
  }) {
    # Will contain deleted object
    changedUser {
      id
      username
      email
    }
  }
}

Operations on many-to-many relationships

Many-to-many relationships can not be updated using the create/replace mutations. Instead, every many-to-many relationship has mutations to add or remove items from them. The name takes the form of add<Type1>From<Type2><ConnectionName>, e.g. addMicropostToUserFavorites or removeUserFromUserFriends. Note that relationships have mutations for both of the connection fields, so there will be both AddMicropostToUserFavorites and AddUserToMicropostFavoritedBy. They are equivalent, they always add or remove items from both connections and are provided for convenience.

Adding item to a relationship that already has it doesn’t produce an error, as won’t removing item from a relationship that doesn’t include it.

Inputs

Inputs for both operations have the following format:

type _Type1Type2ConnectionNameConnectionInput {
  clientMutationId: String
  type1Id: ID!
  type2Id: ID!
}

# For example

type _MicropostUserFavoritesConnectionInput {
  clientMutationId: String
  micropostId: ID!,
  userId: ID!
}

# If connecting the same type

type _UserFriendsConnectionInput {
  clientMutationId: String
  user1Id: ID!,
  user2Id: ID!
}

Payloads

Payload for both operations has the following format:

type _Type1Type2ConnectionNamePayload {
  clientMutationId: String
  changedType1: Type1
  changedType1Edge: _Type1Edge
  changedType2: Type2
  changedType2Edge: _Type2Edge
}

# For example

type _MicropostUserFavoritesPayload {
  clientMutationId: String
  changedMicropost: Micropost
  changedMicropostEdge: _MicropostEdge
  changedUser: User
  changedUserEdge: _UserEdge,
}

# If connecting the same type

type _UserFriendsPayload {
  clientMutationId: String
  changedUser1: User
  changedUser1Edge: _UserEdge
  changedUser2: User
  changedUser2Edge: _UserEdge
}

Operations

addType1ToType2ConnectionName

mutation addTypeToType2ConnectionName(input: _Type1Type2ConnectionNameConnectionInput): _Type1Type2ConnectionNamePayload

For example:

mutation addMicropostToUserFavorites(input: _MicropostUserFavoritesConnectionInput): _MicropostUserFavoritesPayload
mutation addUserToUserFriends(input: _UserFriendsConnectionInput): _UserFriendsPayload

Add node defined by type1Id to connection connectionName of node defined by type2Id.

mutation {
  addMicropostToUserFavorites(input: {
    micropostId: "some-micropost-id",
    userId: "some-user-id"
  }) {
    changedMicropost {
      favoritedBy {
        count
      }
    }
    changedUser {
      favorites {
        count
      }
    }
  }
}

removeType1FromType2ConnectionName

mutation removeTypeFromType2ConnectionName(input: _Type1Type2ConnectionNameConnectionInput): _Type1Type2ConnectionNamePayload

For example:

mutation removeMicropostFromUserFavorites(input: _MicropostUserFavoritesConnectionInput): _MicropostUserFavoritesPayload
mutation removeUserFromUserFriends(input: _UserFriendsConnectionInput): _UserFriendsPayload

Remove node defined by type1Id from connection connectionName of node defined by type2Id.

mutation {
  removeMicropostFromUserFavorites(input: {
    micropostId: "some-micropost-id",
    userId: "some-user-id"
  }) {
    changedMicropost {
      favoritedBy {
        count
      }
    }
    changedUser {
      favorites {
        count
      }
    }
  }
}

Migration API

While most of the time, one would use reindex-cli schema-push to perform migration, you could also use the low-level GraphQL mutation API directly.

migrate

mutation migrate(input: ReindexMigrationInput!): ReindexMigrationPayload

input ReindexMigrationInput {
  types: [ReindexTypeInput]
  force: Boolean
  dryRun: Boolean
  clientMutationId: String
}

type ReindexMigrationPayload {
  clientMutationId: String
  commands: [ReindexMigrationCommand]
  isExecuted: Boolean
}

type ReindexMigrationCommand {
  commandType: String
  isDestructive: Boolean
  description: String
}

Performs a schema migration.

  • types is the list of types in the desired Reindex schema.
  • force - must be true to run the migration, if it includes destructive commands.
  • dryRun - if true, we only validate the schema and return the migration commands without running them.

Output is a list of commands and whether they have been performed.

Topics:

Mutations

Reindex mutations allow you to perform basic create, update, delete operations on nodes and also add and remove nodes in the many-to-many relationships.

All Reindex mutations share several common traits - they all have one argument called input, that holds one optional field clientMutationId. Mutations return a payload object, which also always contains a clientMutationId.

If clientMutationId is passed to input it will always be returned as-is in the payload. This way client libraries, like Relay, can tell results of different mutations apart.

Create, Update, Replace, Delete

Those basic operations share payloads and have similar input formats. They accept all fields that can be modified in the node. The difference is that update and replace also require id of the node being modified in the input, while create doesn’t allow it. delete just accepts the id of the type in the input.

Payload types are generated from the types being modified by the mutation. Their structure is the same for all the mutations of that type.

type _XPayload {
  clientMutationId: String
  id: ID
  changedX: X
  changedXEdge: _XEdge

  viewer: ReindexViewer
  # Fields with Nodes to which Node fields of the type point
  y: Y,
  # for example
  user: User,
}

As payloads need to support various mutation types of Relay, there are multiple fields included in them. In addition to clientMutationId, there is:

  • id - the ID of the mutated node

  • changedX - the mutated node

  • changedXEdge - a connection edge containg the mutated node. Can be used to add a newly created node to a connection in Relay.

  • viewer - The global viewer object. Can be used on the client to add a newly created node to the connection of all nodes of the type.

    E.g. when creating a Todo object, you can add it to viewer.allTodos using the following Relay mutation config:

    {
      type: 'RANGE_ADD',
      parentID: this.props.viewer.id,
      connectionName: 'allTodos',
      edgeName: 'changedTodoEdge',
      rangeBehaviors: {
        '': 'prepend',
      },
    }
    
  • All the node reference fields in this type. These can be used to refer to the parent node for RANGE_ADD mutations in Relay.

See this guide for more information about Relay mutations.

createX

mutation createX(input: _CreateXInput): _XPayload

Create a node of some type. input is a flat object with all the fields of the type alongside clientMutationId. All nonNull fields are required.

mutation {
  createUser(input: {
    username: "freiksenet",
    email: "freiksenet@example.com"
  }) {
    changedUser {
      id
      username
      email
    }
  }
}

updateX

mutation updateX(input: _UpdateXInput): _XPayload

Update the given node. Given fields are merged into existing object. input is a flat object with all the fields of the type alongside clientMutationId and id. id is an id of the node being updated and it is required.

mutation {
  updateUser(input: {
    id: "some-user-id"
    username: "freiksenets-new-name",
  }) {
    changedUser {
      id
      username
    }
  }
}

replaceX

mutation replaceX(input: _ReplaceXInput): _XPayload

Replaces the given node with a new one. input is a flat object with all the fields of the type alongside clientMutationId and id. id is an id of the node being replaced and it is required. All nonNull fields are required.

mutation {
  replaceUser(input: {
    id: "some-user-id"
    username: "freiksenets-new-name",
    email: "freiksenet-new@example.com"
  }) {
    changedUser {
      id
      username
      email
    }
  }
}

deleteX

mutation deleteX(input: _DeleteXInput): _XPayload

Delete the given node. input is an object with fields clientMutationId and id. id is an id of the node being deleted and it is required.

mutation {
  deleteUser(input: {
    id: "some-user-id"
  }) {
    # Will contain deleted object
    changedUser {
      id
      username
      email
    }
  }
}

Operations on many-to-many relationships

Many-to-many relationships can not be updated using the create/replace mutations. Instead, every many-to-many relationship has mutations to add or remove items from them. The name takes the form of add<Type1>From<Type2><ConnectionName>, e.g. addMicropostToUserFavorites or removeUserFromUserFriends. Note that relationships have mutations for both of the connection fields, so there will be both AddMicropostToUserFavorites and AddUserToMicropostFavoritedBy. They are equivalent, they always add or remove items from both connections and are provided for convenience.

Adding item to a relationship that already has it doesn’t produce an error, as won’t removing item from a relationship that doesn’t include it.

Inputs

Inputs for both operations have the following format:

type _Type1Type2ConnectionNameConnectionInput {
  clientMutationId: String
  type1Id: ID!
  type2Id: ID!
}

# For example

type _MicropostUserFavoritesConnectionInput {
  clientMutationId: String
  micropostId: ID!,
  userId: ID!
}

# If connecting the same type

type _UserFriendsConnectionInput {
  clientMutationId: String
  user1Id: ID!,
  user2Id: ID!
}

Payloads

Payload for both operations has the following format:

type _Type1Type2ConnectionNamePayload {
  clientMutationId: String
  changedType1: Type1
  changedType1Edge: _Type1Edge
  changedType2: Type2
  changedType2Edge: _Type2Edge
}

# For example

type _MicropostUserFavoritesPayload {
  clientMutationId: String
  changedMicropost: Micropost
  changedMicropostEdge: _MicropostEdge
  changedUser: User
  changedUserEdge: _UserEdge,
}

# If connecting the same type

type _UserFriendsPayload {
  clientMutationId: String
  changedUser1: User
  changedUser1Edge: _UserEdge
  changedUser2: User
  changedUser2Edge: _UserEdge
}

Operations

addType1ToType2ConnectionName

mutation addTypeToType2ConnectionName(input: _Type1Type2ConnectionNameConnectionInput): _Type1Type2ConnectionNamePayload

For example:

mutation addMicropostToUserFavorites(input: _MicropostUserFavoritesConnectionInput): _MicropostUserFavoritesPayload
mutation addUserToUserFriends(input: _UserFriendsConnectionInput): _UserFriendsPayload

Add node defined by type1Id to connection connectionName of node defined by type2Id.

mutation {
  addMicropostToUserFavorites(input: {
    micropostId: "some-micropost-id",
    userId: "some-user-id"
  }) {
    changedMicropost {
      favoritedBy {
        count
      }
    }
    changedUser {
      favorites {
        count
      }
    }
  }
}

removeType1FromType2ConnectionName

mutation removeTypeFromType2ConnectionName(input: _Type1Type2ConnectionNameConnectionInput): _Type1Type2ConnectionNamePayload

For example:

mutation removeMicropostFromUserFavorites(input: _MicropostUserFavoritesConnectionInput): _MicropostUserFavoritesPayload
mutation removeUserFromUserFriends(input: _UserFriendsConnectionInput): _UserFriendsPayload

Remove node defined by type1Id from connection connectionName of node defined by type2Id.

mutation {
  removeMicropostFromUserFavorites(input: {
    micropostId: "some-micropost-id",
    userId: "some-user-id"
  }) {
    changedMicropost {
      favoritedBy {
        count
      }
    }
    changedUser {
      favorites {
        count
      }
    }
  }
}

Migration API

While most of the time, one would use reindex-cli schema-push to perform migration, you could also use the low-level GraphQL mutation API directly.

migrate

mutation migrate(input: ReindexMigrationInput!): ReindexMigrationPayload

input ReindexMigrationInput {
  types: [ReindexTypeInput]
  force: Boolean
  dryRun: Boolean
  clientMutationId: String
}

type ReindexMigrationPayload {
  clientMutationId: String
  commands: [ReindexMigrationCommand]
  isExecuted: Boolean
}

type ReindexMigrationCommand {
  commandType: String
  isDestructive: Boolean
  description: String
}

Performs a schema migration.

  • types is the list of types in the desired Reindex schema.
  • force - must be true to run the migration, if it includes destructive commands.
  • dryRun - if true, we only validate the schema and return the migration commands without running them.

Output is a list of commands and whether they have been performed.