{"id":13726374,"url":"https://github.com/chrisbolin/understanding-relay-mutations","last_synced_at":"2025-04-14T02:31:52.243Z","repository":{"id":83965448,"uuid":"50147992","full_name":"chrisbolin/understanding-relay-mutations","owner":"chrisbolin","description":"A guide to Relay Mutations via the ToDo MVC","archived":false,"fork":false,"pushed_at":"2016-08-24T16:59:32.000Z","size":2809,"stargazers_count":52,"open_issues_count":2,"forks_count":3,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-27T16:50:45.948Z","etag":null,"topics":["graphql","mutations","relay"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chrisbolin.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2016-01-22T01:05:51.000Z","updated_at":"2024-03-23T12:31:08.000Z","dependencies_parsed_at":"2023-03-02T16:01:20.202Z","dependency_job_id":null,"html_url":"https://github.com/chrisbolin/understanding-relay-mutations","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisbolin%2Funderstanding-relay-mutations","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisbolin%2Funderstanding-relay-mutations/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisbolin%2Funderstanding-relay-mutations/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisbolin%2Funderstanding-relay-mutations/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chrisbolin","download_url":"https://codeload.github.com/chrisbolin/understanding-relay-mutations/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248810885,"owners_count":21165196,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["graphql","mutations","relay"],"created_at":"2024-08-03T01:03:01.887Z","updated_at":"2025-04-14T02:31:51.807Z","avatar_url":"https://github.com/chrisbolin.png","language":null,"funding_links":[],"categories":["Others"],"sub_categories":[],"readme":"_incomplete, but you'll forgive me_ 🌈\n\n# Understanding Relay Mutations\n\nMutations are arguably the most difficult feature of Relay to learn. This is a walkthrough of mutations using the official [Todo MVC example](https://github.com/facebook/relay/tree/master/examples/todo) that's also included with this guide.\n\n### Prerequisites\n\nTo get the most out of this walkthrough, you'll probably want to have a good grasp on a few topics...\n\n* **GraphQL.** [Learn GraphQL](https://learngraphql.com/), the [GraphQL homepage](http://graphql.org/), and [Thinking in GraphQL](https://facebook.github.io/relay/docs/thinking-in-graphql.html) are great resources. After using them, you should be familiar with [GraphiQL](https://github.com/graphql/graphiql).\n* **Relay.** Read through [Thinking in Relay](https://facebook.github.io/relay/docs/thinking-in-relay.html) and complete the [Relay Tutorial](https://facebook.github.io/relay/docs/tutorial.html). Of course, it's expected that Relay mutations are still a mystery to you, or you probably wouldn't be reading this.\n* **React.** Go build a simple React app. Read [Thinking in React](https://facebook.github.io/react/docs/thinking-in-react.html).\n* **NodeJS.** [Install it](https://nodejs.org/en/) and maybe do a [quick tutorial](http://blog.modulus.io/absolute-beginners-guide-to-nodejs).\n\n### Running the Example\n\nThe Todo example in this repo is a slightly modified version of the [original](https://github.com/facebook/relay/tree/master/examples/todo) (for starters, GraphiQL is enabled and all local Relay dependencies are replaced).\n\n```\ngit clone https://github.com/chrisbolin/understanding-relay-mutations\ncd understanding-relay-mutations/examples/todo\nnpm install\nnpm start\n```\n\nAfter the servers start navigate to [localhost:3000](http://localhost:3000) to view the app and [localhost:3000/graphql](http://localhost:3000/graphql) to play with GraphiQL. I'd recommend opening them both in separate tabs.\n\nThis example stores its data in the server's memory. Therefore, reloading the app page will not discard any saved changes, but restarting the server will reset the data.\n\n## The Anatomy of Relay Mutations\n\nMutations allow us to use a GraphQL query to change our data. All mutations consist of three pieces:\n\n1. Mutation schema on the **GraphQL server**\n1. Relay Mutation class on the **Relay client**\n1. Relay Store update on the **Relay client**\n\nWe'll look at each step in the process using the simplest mutation in our example app, _renaming a todo_.\n\n#### 1. Mutation Schema (Server)\n\nThis is the best place to start creating a mutation. The mutation schema on the server is where we define how the mutation should be called (**inputs**), what it does to our data (**mutations**), and what it returns (**outputs**). With just this mutation schema we can successfully call the mutation with a simple HTTP request or GraphiQL.\n\n##### *`mutationWithClientMutationId(params)`*\n\nRelay mutations are GraphQL mutations with a few extra requirements. We will use `mutationWithClientMutationId()` from the `graphql-relay` package to help create a Relay-compliant GraphQL mutation. This function accepts an object with four parameters and creates GraphQL mutation.\n\n**The Code**\n\nBelow is the full code for creating the mutation with `mutationWithClientMutationId`. We'll cover each piece in detail below.\n\n**File:** `todo/data/schema.js`\n\n```javascript\nvar GraphQLRenameTodoMutation = mutationWithClientMutationId({\n  name: 'RenameTodo',\n  inputFields: {\n    id: { type: new GraphQLNonNull(GraphQLID) },\n    text: { type: new GraphQLNonNull(GraphQLString) },\n  },\n  outputFields: {\n    todo: {\n      type: GraphQLTodo,\n      resolve: ({localTodoId}) =\u003e getTodo(localTodoId),\n    },\n  },\n  mutateAndGetPayload: ({id, text}) =\u003e {\n    var localTodoId = fromGlobalId(id).id;\n    renameTodo(localTodoId, text);\n    return {localTodoId};\n  },\n});\n```\n\nThere are two more small pieces of bookkeeping, which will be familiar to you from queries in GraphQL: creating the parent mutation GraphQL Object and exporting the GraphQL schema.\n\n```javascript\nvar Mutation = new GraphQLObjectType({\n  name: 'Mutation',\n  fields: {\n    renameTodo: GraphQLRenameTodoMutation,\n    ...\n  },\n});\n\nexport var schema = new GraphQLSchema({\n  query: Root,\n  mutation: Mutation,\n});\n```\n\nThese importantly determine the path we'll call the mutation from:\n```python\nmutation { # from the GraphQLSchema\n\trenameTodo # from the Mutation GraphQLObjectType\n}\n```\n\n**Parameters for `mutationWithClientMutationId`**\n\n`name` (String)\n\nThe name of the mutation is important. `mutationWithClientMutationId` will create a payload type using this name suffixed with \"Payload\"; for example a mutation with the name `RenameTodo` creates an output payload with the name `RenameTodoPayload`. This payload type will be used by the client.\n\n`inputFields` (Object)\n\nThe `inputFields` object should feel familiar to other GraphQL fields, for example the `fields` object used when creating a new `GraphQLObjectType` instance. Each field inside of `inputFields` is an object with a `type` and an optional `description`. For example, here's the `RenameTodo` mutation's `inputFields`:\n\n```javascript\ninputFields: {\n  id: { type: new GraphQLNonNull(GraphQLID) },\n  text: { type: new GraphQLNonNull(GraphQLString) },\n}\n```\n\nIt's important to note that one other input will be added to this mutation by `mutationWithClientMutationId`: a required String input, `clientMutationId`. This ID is generated by the Relay client behind the scenes to track the mutation's progress.\n\nAll Relay mutations have only one input, conveniently named `input`; this is an object that holds all of the `inputFields` plus `clientMutationId`. (This is not a GraphQL requirement, but rather an extra Relay standard.)\n\n**Calling the Mutation without Relay**\n\nTo try out the mutation without Relay, go to GraphiQL (at [localhost:3000/graphql](http://localhost:3000/graphql)). We'll rename the first todo \"Taste Javascript\" to \"Get Cereal\". Remember, the clientMutationId input is required by the mutation; don't worry, we can spoof it when we're interacting with the mutation outside of Relay.\n\n```python\nmutation {\n  renameTodo(input: {id: \"VG9kbzow\" text: \"Get Cereal\" clientMutationId: \"1\"}) {\n    todo {\n      text\n    }\n  }\n}\n```\n\nYou should this response back (but if you previously deleted the todo with the ID `VG9kbzow` you'll get an error)...\n\n```javascript\n{\n  \"data\": {\n    \"renameTodo\": {\n      \"todo\": {\n        \"text\": \"Get Cereal\"\n      }\n    }\n  }\n}\n```\n\n Now go to the app at [localhost:3000](http://localhost:3000) and refresh it to see the renamed todo. (As an aside, you have to refresh the app because Relay [doesn't yet](https://github.com/facebook/relay/issues/541) support \"subscribing\" to updates from other clients.)\n\n#### 2. Relay.Mutation (Client)\n\nThe `Relay.Mutation` class must be extended to call the mutation from the client. This is the most complicated and confusing aspect of Relay mutations. If you are struggling to get a mutation to work, chances are that your problem lies here.\n\n**File:** `todo/js/mutations/RenameTodoMutation.js`\n\n```javascript\nexport default class RenameTodoMutation extends Relay.Mutation {\n  // we only need to request the id to perform this mutation\n  static fragments = {\n    todo: () =\u003e Relay.QL`\n      fragment on Todo {\n        id,\n      }\n    `,\n  };\n  getMutation() {\n    return Relay.QL`mutation{renameTodo}`;\n  }\n  getFatQuery() {\n    return Relay.QL`\n      fragment on RenameTodoPayload {\n        todo {\n          text,\n        }\n      }\n    `;\n  }\n  getConfigs() {\n    return [{\n      type: 'FIELDS_CHANGE',\n      fieldIDs: {\n        todo: this.props.todo.id,\n      },\n    }];\n  }\n  getVariables() {\n    // there are two inputs to the mutation: the todo `id` and the new `text` name.\n    return {\n      id: this.props.todo.id,\n      text: this.props.text,\n    };\n  }\n  getOptimisticResponse() {\n    // optionally spoof the server response\n    // same output as the above outputFields()\n    return {\n      todo: {\n        id: this.props.todo.id,\n        text: this.props.text,\n      },\n    };\n  }\n}\n```\n\n#### 3. Relay Store Update (Client)\n\nThe `Relay.Mutation` subclass you create needs to be instantiated and sent to `Relay.Store.commitUpdate()` to run the mutation from the Relay client. The mutation will be sent to the server, carried out, and the results will be updated in the Relay client store.\n\n**File:** `todo/js/components/Todo.js`\n\n```javascript\n_handleTextInputSave = (text) =\u003e {\n  Relay.Store.commitUpdate(\n    new RenameTodoMutation({todo: this.props.todo, text})\n  );\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchrisbolin%2Funderstanding-relay-mutations","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchrisbolin%2Funderstanding-relay-mutations","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchrisbolin%2Funderstanding-relay-mutations/lists"}