1
votes

I have two GraphQL/Relay mutations that work fine separately. The first one creates an item. The second one runs a procedure for connecting two items.

GraphQL

createOrganization(
  input: CreateOrganizationInput!
): CreateOrganizationPayload

createOrganizationMember(
  input: CreateOrganizationMemberInput!
): CreateOrganizationMemberPayload

input CreateOrganizationInput {
  clientMutationId: String
  organization: OrganizationInput!
}

input CreateOrganizationMemberInput {
  clientMutationId: String
  organizationMember: OrganizationMemberInput!
}

# Represents a user’s membership in an organization.
input OrganizationMemberInput {
  # The organization which the user is a part of.
  organizationId: Uuid!

  # The user who is a member of the given organization.
  memberId: Uuid!
}

type CreateOrganizationPayload {
  clientMutationId: String

  # The `Organization` that was created by this mutation.
  organization: Organization

  # An edge for our `Organization`. May be used by Relay 1.
  organizationEdge(
    orderBy: OrganizationsOrderBy = PRIMARY_KEY_ASC
  ): OrganizationsEdge

  # Our root query field type. Allows us to run any query from our mutation payload.
  query: Query
}

I would like to be able to run the createOrganization mutation and then connect the user to the organization with the createOrganizationMember mutation. The second mutation takes two arguments, one of which is the newly created edge.

I tried passing the edge into the mutation, but it expects the mutation to be able to getFragment. How can I get the fragment for the payload edge so it can be passed into a mutation?

React-Relay

Relay.Store.commitUpdate(
      new CreateOrganizationMutation({
        organizationData: data,
        user,
        query,
      }), {
        onSuccess: response => {
          Relay.Store.commitUpdate(
            new CreateOrganizationMemberMutation({
              organization: response.createOrganization.organizationEdge.node,
              user,
            })
          );
        },
      }
    );

  fragments: {
    user: () => Relay.QL`
      fragment on User {
        ${CreateOrganizationMutation.getFragment('user')},
        ${CreateOrganizationMemberMutation.getFragment('user')},
      }
    `,
1

1 Answers

0
votes

I solved this problem without changing any GraphQL:

I created a new Relay container, route, and queries object. It is configured as a child route for the container where the first of two mutation occurs. The id for the new edge is passed as a parameter via the route pathname. A router state variable is also passed.

Routes

import {Route} from 'react-router';

function prepareProfileParams (params, {location}) {
  return {
    ...params,
    userId: localStorage.getItem('user_uuid'),
  };
}
    // ProfileContainer has the component CreateOrganizationForm, which calls
    // the createOrganization mutation
    <Route
      path={'profile'}
      component={ProfileContainer}
      queries={ProfileQueries}
      prepareParams={prepareProfileParams}
      onEnter={loginBouncer}
      renderLoading={renderLoading}
    >
      <Route path={'join-organization'}>
        <Route
          path={':organizationId'}
          component={JoinOrganizationContainer}
          queries={JoinOrganizationQueries}
          renderLoading={renderLoading}
        />
      </Route>
    </Route>

CreateOrganizationForm.js

    Relay.Store.commitUpdate(
      new CreateOrganizationMutation({
        organizationData: data,
        user,
        query,
      }), {
        onSuccess: response => {
          const organizationId = response.createOrganization.organizationEdge.node.rowId;
          router.push({
            pathname: `/profile/join-organization/${organizationId}`,
            state: {
              isAdmin: true,
            },
          });
        },
      }
    );

The new Relay container JoinOrganizationContainer will hook into a lifecycle method to call the second mutation that we needed. The second mutation has an onSuccess callback which does router.push to the page for the new object we created with the first mutation.

JoinOrganizationContainer.js

    import React from 'react';
    import Relay from 'react-relay';
    import CreateOrganizationMemberMutation from './mutations/CreateOrganizationMemberMutation';

    class JoinOrganizationContainer extends React.Component {
      static propTypes = {
        user: React.PropTypes.object,
        organization: React.PropTypes.object,
      };
      static contextTypes = {
        router: React.PropTypes.object,
        location: React.PropTypes.object,
      };
      componentWillMount () {
        const {user, organization} = this.props;
        const {router, location} = this.context;

        Relay.Store.commitUpdate(
          new CreateOrganizationMemberMutation({
            user,
            organization,
            isAdmin: location.state.isAdmin,
          }), {
            onSuccess: response => {
              router.replace(`/organization/${organization.id}`);
            },
          }
        );
      }
      render () {
        console.log('Joining organization...');
        return null;
      }
    }

    export default Relay.createContainer(JoinOrganizationContainer, {
      initialVariables: {
        userId: null,
        organizationId: null,
      },
      fragments: {
        user: () => Relay.QL`
          fragment on User {
            ${CreateOrganizationMemberMutation.getFragment('user')},
          }
        `,
        organization: () => Relay.QL`
          fragment on Organization {
            id,
            ${CreateOrganizationMemberMutation.getFragment('organization')},
          }
        `,
      },
    });

JoinOrganizationQueries.js

    import Relay from 'react-relay';

    export default {
      user: () => Relay.QL`
        query { userByRowId(rowId: $userId) }
      `,
      organization: () => Relay.QL`
        query { organizationByRowId(rowId: $organizationId) }
      `,
    };

One unexpected benefit of doing things this way is that there is now a shareable url that can be used as an invite link for joining an organization in this app. If the user is logged in and goes to the link: <host>/profile/join-organization/<organizationRowId>, the mutation will run that joins the person as a member. In this use case, router.state.isAdmin is false, so the new membership will be disabled as an admin.