Apollo Elements Guides API Blog

Building Apps: Mutations

Mutations are how you change data in your graphql. If you think of queries as analogous to HTTP GET requests or SQL READ statements, then mutations are kind of like HTTP POST requests or SQL WRITE statements.

Mutation Components

Unlike query and subscription components, mutation components don't automatically send a request to the GraphQL server. You have to call their mutate() method to issue the mutation, typically in response to some user input.

As such, you can only expect your component's data property to be truthy once the mutation resolves.

import type { ResultOf, VariablesOf } from '@graphql-typed-document-node/core';

import { ApolloMutationMixin } from '@apollo-elements/mixins/apollo-mutation-mixin';

import { AddUserMutation } from './AddUser.mutation.graphql';

const template = document.createElement('template');
template.innerHTML = `
  <p-card>
    <h2 slot="heading">Add User</h2>

    <dl hidden>
      <dt>Name</dt>  <dd data-field="name"></dd>
      <dt>Added</dt> <dd data-field="timestamp"></dd>
    </dl>

    <mwc-textfield slot="actions" label="User Name" outlined></mwc-textfield>
    <mwc-button slot="actions" label="Add User"></mwc-button>
  </p-card>
`;

export class AddUserElement extends ApolloMutation<typeof AddUserMutation> {
  mutation = AddUserMutation;

  #data: ResultOf<typeof AddUserMutation>;
  get data(): ResultOf<typeof AddUserMutation> { return this.#data; }
  set data(data: ResultOf<typeof AddUserMutation>) { this.render(this.#data = data); }

  $(selector) { return this.shadowRoot.querySelector(selector); }

  constructor() {
    super();
    this
      .attachShadow({ mode: 'open' })
      .append(template.content.cloneNode(true));
    this.onInput = this.onInput.bind(this);
    this.$('mwc-textfield').addEventListener('click', this.onInput);
    this.$('mwc-button').addEventListener('click', () => this.mutate());
  }

  onInput({ target: { value: name } }) {
    this.variables = { name };
  }

  render(data) {
    this.$('dl').hidden = !!data;
    if (data) {
      const timestamp = new Date(data.timestamp).toDateString();
      const { name } = data;
      for (const [key, value] of Object.entries({ name, timestamp })
        this.$(`[data-field="${key}"]`).textContent = value;
    }
  }
}

customElements.define('add-user', component(AddUser));
import { ApolloMutation } from '@apollo-elements/lit-apollo/apollo-mutation';
import { customElement, html, TemplateResult } from 'lit-element';

import { AddUserMutation } from './AddUser.mutation.graphql';

@customElement('add-user')
export class AddUserElement extends ApolloMutation<typeof AddUserMutation> {
  mutation = AddUserMutation;

  render(): TemplateResult {
    const name = this.data.name ?? '';
    const timestamp = this.data ? new Date(this.data.timestamp).toDateString() : '';
    return html`
      <p-card>
        <h2 slot="heading">Add User</h2>

        <dl ?hidden="${!this.data}">
          <dt>Name</dt>  <dd>${name}</dd>
          <dt>Added</dt> <dd>${timestamp}</dd>
        </dl>

        <mwc-textfield slot="actions"
            label="User Name"
            outlined
            @input="${this.onInput}"></mwc-textfield>
        <mwc-button slot="actions"
            label="Add User"
            @input="${() => this.mutate()}"></mwc-button>
      </p-card>
    `;
  }

  onInput({ target: { value: name } }) {
    this.variables = { name };
  }
}
import { ApolloMutation } from '@apollo-elements/fast/apollo-mutation';

import { customElement, html } from '@microsoft/fast-element';

import { AddUserMutation } from './AddUser.mutation.graphql';

import type { Binding } from '@microsoft/fast-element';

const getName: Binding<AddUserElement> =
   x =>
     x.data?.name ?? ''

const getTimestamp: Binding<AddUserElement> =
  x =>
    x.data ? new Date(x.data.timestamp).toDateString() : '';

const setVariables: Binding<AddUserElement) =
  (x, { target: { value: name } }) => {
    x.variables = { name };
  }

@customElement({
  name: 'add-user'
  template: html<AddUserElement>`
    <p-card>
      <h2 slot="heading">Add User</h2>

      <dl ?hidden="${x => !x.data}">
        <dt>Name</dt>  <dd>${getName}</dd>
        <dt>Added</dt> <dd>${getTimestamp}</dd>
      </dl>

      <mwc-textfield slot="actions"
          label="User Name"
          outlined
          @input="${setVariables}"></mwc-textfield>
      <mwc-button slot="actions"
          label="Add User"
          @input="${x => x.mutate()}"></mwc-button>
    </p-card>
  `,
})
export class AddUserElement extends ApolloMutation<typeof AddUserMutation> {
  mutation = AddUserMutation;
}
import { useMutation } from '@apollo-elements/haunted/useMutation';
import { useState, component, html } from 'haunted';
import { AddUserMutation } from './AddUser.mutation.graphql';

function AddUser() {
  const [addUser, { called, data }] = useMutation(AddUserMutation);
  const [variables, setVariables] = useState({ });

  const onInput = event => setVariables({ name: event.target.value }));

  const name = data.name ?? '';
  const timestamp = data ? new Date(data.timestamp).toDateString() : '';

  return html`
    <p-card>
      <h2 slot="heading">Add User</h2>

      <dl ?hidden="${!data}">
        <dt>Name</dt>  <dd>${name}</dd>
        <dt>Added</dt> <dd>${timestamp}</dd>
      </dl>

      <mwc-textfield slot="actions"
          label="User Name"
          outlined
          @input="${onInput}"></mwc-textfield>
      <mwc-button slot="actions"
          label="Add User"
          @input="${() => addUser({ variables })}"></mwc-button>
    </p-card>
  `;
}

customElements.define('add-user', component(AddUser));
import { client, mutation, define, html } from '@apollo-elements/hybrids';

import { AddUserMutation } from './AddUser.mutation.graphql';

const onInput =
  (host, event) =>
    setVariables({ name: event.target.value }));

const mutate =
  host =>
    host.mutate();

define<ApolloMutationElement<typeof AddUserMutation>('add-user', {
  client: client(),
  mutation: mutation(AddUserMutation),
  render: ({ host }) => {
    const name = host.data.name ?? '';
    const timestamp = host.data ? new Date(host.data.timestamp).toDateString() : '';
    return html`
      <p-card>
        <h2 slot="heading">Add User</h2>

        <dl ?hidden="${!host.data}">
          <dt>Name</dt>  <dd>${name}</dd>
          <dt>Added</dt> <dd>${timestamp}</dd>
        </dl>

        <mwc-textfield slot="actions"
            label="User Name"
            outlined
            @input="${onInput}"></mwc-textfield>
        <mwc-button slot="actions"
            label="Add User"
            @input="${mutate}"></mwc-button>
      </p-card>
    `;
  },
})

The key here is the <mwc-button> element which, on click, calls the element's mutate() method.

See mutation lifecycle for more information.