Mutations in Relay Modern

January 06, 2018

In this series, I covered various topics starting from setting up Django and ending in Relay queries. There is only one topic to be covered - mutations. And today I want to tell you how to implement them.

What is mutation

You can think of mutation of a request that changes data. In REST terms it will be POST, PUT or DELETE. In addition to this in Relay you can request your data back - It means that to display newly created entity you don’t need an additional query.

Mutation looks as follows:

mutation CreateFilmMutation($input: CreateFilmInput!) {
    createFilm(input: $input) {
      film {
        id
        title
        rating
        airDate
        actors {
          firstName
          lastName
        }
      }
    }
  }

In such case, I provide inputs to mutation. For film it will be a title, air date and rating. In definition I also add a query for getting created data back.

How to implement mutation

As I got simple mutation definition it’s time to implement it. First I need to create a way to execute my mutation. I decided to have a form where a user can fill title, air date and rating. When she or he submits - I will execute mutation. It looks as follows:

export default class AddFilmForm extends Component {
  constructor(props) {
    super(props);

    this.state = {
      title: "",
      date: "",
      rating: 0
    };
  }

  handleChange = event => {
    const target = event.target;
    const name = target.name;
    const value =
      target.type === "select-one" ? parseInt(target.value, 10) : target.value;
    this.setState({
      [name]: value
    });
  };

  handleSubmit = () => {
    CreateFilmMutation(this.state.title, this.state.date, this.state.rating);
  };

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <FormGroup>
          <ControlLabel>Enter details of new film</ControlLabel>
          <FormControl
            type="text"
            name="title"
            value={this.state.title}
            placeholder="Title"
            onChange={this.handleChange}
          />
        </FormGroup>
        <FormGroup>
          <ControlLabel>Enter details of new film</ControlLabel>
          <FormControl
            type="date"
            name="date"
            value={this.state.date}
            placeholder="Air date"
            onChange={this.handleChange}
          />
        </FormGroup>
        <FormGroup onChange={this.handleChange}>
          <ControlLabel>Select rating</ControlLabel>
          <FormControl
            componentClass="select"
            placeholder="select"
            name="rating"
          >
            <option value="0">0</option>
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
            <option value="4">4</option>
          </FormControl>
        </FormGroup>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

As you can see this is a normal implementation of how to handle forms in React. I catch onChange events in handleChange then get value character by character and store in the component state. At the end when user clicks submit I just fire up CreateFilmMutation inside handleSubmit.

How is this mutation implemented? Look below:

import { commitMutation, graphql } from "react-relay";
import environment from "../Environment";

const mutation = graphql`
  mutation CreateFilmMutation($input: CreateFilmInput!) {
    createFilm(input: $input) {
      film {
        id
        title
        rating
        airDate
        actors {
          firstName
          lastName
        }
      }
    }
  }
`;

export default function CreateFilmMutation(title, airDate, rating) {
  const variables = {
    input: {
      title,
      airDate,
      rating,
      actors: [{ actorId: "QWN0b3I6MQ==" }]
    }
  };
  commitMutation(environment, {
    mutation,
    variables,
    onCompleted: (response, errors) => {
      console.log("Response received from server.");
    },
    onError: err => console.error(err)
  });
}

At the top, I have my mutation syntax which then will be executed at the graphql endpoint. In my variables definition I hardcode actors which can be changed by tweaking a little bit form.

As you can see a mutation in executed in commitMutation with two callbacks: one for completing mutation and the second one when mutation end with an error. This is a useful place to show for example errors to the user or some confirmation.

That’s all! I have my mutation working. Thank you for reading that and if you like it please share it on interwebs.

Repo with code can be found on Github.