Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

A guide to learn about Relay mutations.

License

NotificationsYou must be signed in to change notification settings

pt-br/relay-mutation-guide

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

47 Commits
 
 
 
 
 
 

Repository files navigation

You Gonna Learn Today!

This guide will fit if you find yourself into one(or more) of the following situations:

  • I'm freaking out about how to handle mutations on GraphQL and Relay;
  • I tried reading someone else's code, but it's not too much explained and it's complex;
  • The examples I found on internet have less explanation and a lots of code that will output something only 1h after going through the tutorial;
  • Star Wars' example of facebook sucks.

Intro

In this guide we will be creating a simple application that uses a real world example to get things easier for you.It's important to say that this is not a complete guide for GraphQL and Relay, the focus of this guide is only onMutations.

So, before you get started, be sure to know at least the basics of:

  • ES6
  • React
  • Relay (how it fits into a React application)
  • GraphQL
  • GraphiQL

Getting the Playground

As I said, we will be working on an application for this guide. You can check it running accessing ademo build on heroku. I've prepared a special boilerplate of this application, removing everything related to mutations, so we can do it together on this guide.

Let's put the hand on the code, start by clonning the boilerplate:

git clone https://github.com/pt-br/relay-phones-guide.git

Go inside the folder you've clonned the repository, and install the node modules:

npm install

Don't worry about the code for now, just start the server by running:

npm start

Go tohttp://localhost:3000 and check if you can see the application running. If not, get back to the first step.

Ok, if you are here you already have the environment running. Let's go ahead.

Understanding what is the Database

On a real application, the database usually would be based into some database management system, REST APIs, etc. For this guide, our database is pure JavaScript.

We have three class files:Database.js,User.js andPhone.js.Database.js is our main entry class for database operations. GraphQL queries and mutations will comunicate with this file to read and change data.User.js is a class that contains its own methods and contains instances ofPhone.js as well.

I'm not going deeper inside of this files, but you can read them if you want - They are pretty well commented and I'm sure you'll easily understand how they work.

PS: GraphQL isnot a database, don't misunderstand that.

What is a Mutation

A mutation is an operation that creates, changes or erases something (for reading, we use queries and not mutations). A mutation will always do something, then fetch something.

It's important to know that a mutation affects both sides of your application: server(GraphQL) and client(Relay).

Creating a Mutation to Add a Phone

Let's start by creating a mutation to add a new phone.

Go through/data/schema.js and observe that we are importing some GraphQL and GraphQL-Relay items in there.

We are also importing ourDatabase.js class and creating an instance of it inside of the schema. This is required in order to make possible doing manipulations into our database by using GraphQL, in other words: GraphQL will be able to access methods inside ofDatabase.js.

Onschema.js, write the following mutation at line 96:

constAddPhoneMutation=mutationWithClientMutationId({name:'AddPhone',inputFields:{model:{type:newGraphQLNonNull(GraphQLString),},image:{type:newGraphQLNonNull(GraphQLString),},},outputFields:{viewer:{type:UserType,resolve:()=>database.getUser(),},},mutateAndGetPayload:({ model, image})=>{constnewPhone=database.insertPhone(model,image);returnnewPhone;},});

Explanation:mutationWithClientMutationId is a helper to build mutations from the packagegraphql-relay. You don't need to know much more about this, you only need to know a few things:

  • It takes a name, as we usedAddPhone
  • It takes inputFields;
  • It takes outPutFields;
  • It takes a mutation method (mutateAndGetPayload).

It's very important to have aname for our mutations. It will be used both on GraphQL and Relay.

On theinputFields we must declare the things we are going to send for the mutation. Once we are going to add a new Phone, we must send amodel and animage fields, that are the required data to create a new Phone into our database.

On theoutputFields we are declaring what will be output by the mutation, in this case, we are just returning our User (that contains Phones).

OnmutateAndGetPayload we are using data provided by theinputFields, in this case,model andimage. Then we call a function from ourDatabase.js calledinsertPhone, we are sending bothmodel andimage to this function. Inside ofinsertPhone, there's a return for the new phone being added, and this is what we are returning onmutateAndGetPayload.

It got simple for you now? I hope so :p

Write a container for our Mutations

Ok, you have a mutation and now you need to tell GraphQL that you do.

Like the Root query, you must specify a "root mutation" that will contain all of your mutations.

Onschema.js at line 134, write your root mutation:

constMutation=newGraphQLObjectType({name:'Mutation',fields:()=>({addPhone:AddPhoneMutation,}),});

I think it's self-explanatory.

Then, at line 145, modify your Schema export to contain the root mutation. It should be like this:

exportconstSchema=newGraphQLSchema({query:Root,  mutation:Mutation,});

Testing our Mutation

Ok, the GraphQL side of the mutation is done!

It's important to notice that every change on theschema.js will require you to stop the server and run a command to update the schema. In order to update our schema, let's do it now.

Stop the server, then run:

npm run update-schema

And then start the server again:

npm start

Alright, your schema is now updated and you can test your mutation.

Let's go to our GraphiQL instance that is running onhttp://localhost:8080, I strongly recommend you to run it on a separate tab.

Run the following mutation on the left pannel:

mutation{addPhone(input:{clientMutationId:"1"model:"Nexus 5",image:"https://goo.gl/Fq46CZ"}){viewer{phones{edges{node{            modelimage}}}}}}

Explanation: We are calling the mutation byaddPhone, the same name we have exported on our Root Mutation. Then, we need to supply the inputs(remember when we declared it on theinputFields?).

  • As you can notice, we are sending a parameter that was not into ourinputFields declaration -clientMutationId. You don't need to worry about this field, it will be automatically filled by Relay under the hood. We only need to spoof it on GraphiQL(and any string value should work).

  • What comes after theaddPhone is theoutputFields stuff.

After adding the Nexus 5 using our mutation, you can refresh the application athttp://localhost:3000 and check that the phone is there.

Yes, your mutation worked pretty good!

Your mutation working with Relay

Probably this is the part of the story you have been always stuck in, but don't worry, this time it will work.

Let's create the Relay part of your mutation - Go to/js/mutations folder and create a file namedAddPhoneMutation.js.

Inside of this file, write the following code:

importRelayfrom'react-relay';exportdefaultclassAddPhoneMutationextendsRelay.Mutation{staticfragments={viewer:()=>Relay.QL`      fragment on User {        id      }    `,};getMutation(){returnRelay.QL`mutation{addPhone}`;}getVariables(){let{ phoneModel, phoneImage}=this.props;/**     * If the fields come empty, force them to be null.     * This way, it won't be filled on GraphQL.     */phoneModel.length===0 ?phoneModel=null :false;phoneImage.length===0 ?phoneImage=null :false;return{model:phoneModel,image:phoneImage,};}getFatQuery(){returnRelay.QL`    fragment on AddPhonePayload {      viewer    }    `;}getConfigs(){const{ viewer}=this.props;return[{type:"FIELDS_CHANGE",fieldIDs:{viewer:viewer.id}}];}}

Explanation: At the first moment it could be strange for you, but it will make sense.

First of all, there's a sequence of the methods execution - I'll be explainig them at the same order they are executed. Also, part of these functions will be responsible tosend data and other part will behandling the application after that the modifications happen.

  • Firstly, we declare a static method calledfragments. This holds the required data to run our mutation - In this case, ourUser id

  • ThegetMutation() method uses theRelay.QL(you can read more about thishere) method to specify which mutation we'll be operating. Remember when we exported our mutation into our Root Mutation? That's what we must use here.

  • OngetVariables() we will be collecting the necessary data to send to ourinputFields. Remember when we create them into our mutation on GraphQL? This function must return an object that matches exactly theinputFields we've declared on theschema. You will be sending this data byprops, inside of your application from your React components. - We'll be reviewing this later.

  • OngetFatQuery() we will be returning a fragment containing everything that could change as a result of this mutation. In our case, what changes here is ourUser(viewer). You can notice that the fragment name of the Fat Query isAddPhonePayload, but this is not declared anywhere - Yes, it isn't. This name is auto generated internally and it just concats the name of your mutation (AddPhone) + Payload. Remember when we declared the name of your mutation on theschema?

  • AtgetConfigs() we advise how Relay should handle theAddPhonePayload returned by the server. In our case, we are using aFIELDS_CHANGE type (because our User has changed, now it has more Phones than before) and we specify theid of what is being changed, ourviewer id (that is theUser id). Here, like we passed theinputFields data togetVariables(), we must pass theviewer as aprop too. - We'll be reviewing this later.

At this point, your mutation should be working fine.

Now, we must configure how we will be sending the necessary data to ourAddPhoneMutation.js (asprops) into our React application, and setting how to call this mutation internally.

Calling our Mutation from React

You've learned a lot until here, and you are almost close to get you mutation working by Relay. Let's adapt out React components to receive and call ourAddPhoneMutation.js.

Go throughviews/PhoneView.js and import our mutation file at line 5:

importAddPhoneMutationfrom'../mutations/AddPhoneMutation';

Scroll down until you reach theRelay Container of this view at line 119. Modify the fragment, including the mutation fragment at line 123. It should look like this:

exportdefaultRelay.createContainer(PhoneView,{fragments:{viewer:()=>Relay.QL`      fragment on User {${AddPhoneMutation.getFragment('viewer')}        phones(first: 908098879) {          edges {            node {              phoneId              model              image            },          },        },      }    `,},});

Ok, now your view is ready to receive the modifications comming from the mutation - It's time to set up how we will trigger it and pass the data asprops.

If you check the line 51 ofPhoneView.js you will notice that we have a component calledAddModal:

<AddModalviewer={viewer}handleModal={this.handleModal}/>

This component receives twoprops:viewer andhandleModal. Theviewer comes from ourRelay Container as a  prop that Relay send to our React components. We are sending this toAddModal because we're going to call our mutation inside of this component - So let's do it.

Go tojs/components/AddModal.js and import our mutation here too, at line 4:

importAddPhoneMutationfrom'../mutations/AddPhoneMutation';

To keep this guide as simple as possible, theAddModal component already have all the logic implemented, so you just need to worry about the mutation process.

Go to line 22, you will notice there’s a function in there calledaddPhone - This function is triggered when user clicks on theAdd button, that is on the modal.

Remove thealert that is inside of this function and let’s start working into passing data to our mutation.

The form inside of this modal has twouncontrolled inputs, their refs arephoneModelInput andphoneImageInput, and we are going to get its values to send as data for our mutation.

We will start declarating some constants inside of our function, so, right below theaddPhone() declaration, write the following code:

const{ viewer}=this.props;const{ phoneModelInput, phoneImageInput}=this.refs;

Now we have three variables:viewer (that we are receiving asprops) andphoneModelInput /phoneImageInput that are our inputs.

Ok, now we need to call our mutation sending the required data to our mutation. For this, we'll be using thecommitUpdate() method fromRelay.Store API. This is one of the methods we can use to trigger a mutation.

ThecommitUpdate() method accepts two callbacks to be triggered after the server response:

  • onSuccess is called if the mutation succeeded.
  • onFailure is called if the mutation failed.

We will be implementing both, so we can close theAddModal when we reachonSuccess or show an error when we got anonFailure callback. In order to do that, let's implementcommitUpdate() into ouraddPhone() function, it should look like this:

addPhone=()=>{const{ viewer}=this.props;const{ phoneModelInput, phoneImageInput}=this.refs;Relay.Store.commitUpdate(newAddPhoneMutation({        viewer,phoneModel:phoneModelInput.value,phoneImage:phoneImageInput.value,}),{onFailure:(transaction)=>{this.setState({error:'Something went wrong, please try again.',});},onSuccess:(response)=>{this.close();},},);};

 Explanation: We are calling thecommitUpdate() from Relay.Store, then we create a new instance of our mutation by usingnew AddPhoneMutation - At this point, we will be sending theprops we need inside our mutation.

  • Theviewer prop will be used inside of the mutation on thegetConfigs() function (Remember that?)

  • ThephoneModel andphoneImage are receiving the value from our inputs and will be used bygetVariables() function inside of the mutation. Then that function will be sending this data to theinputFields we declared at theschema (Makes sense now, right? xD)

  • At theonFailure callback we are setting astate that will just render an error into ourAddModal component (You can reach this callback by clickingAdd without filling the inputs).

  • At theonSuccess callback we just call a function that closes our modal.

Ok then, time to test it! Go tohttp://localhost:3000 and try adding a new Phone.

If you have followed this guide correctly, you should be able to addPhones toUsers, what means thatYOUR MUTATION IS WORKING!

Next Steps

Now that you know exactly how a mutation work, why don't you try to implement two more mutations to delete and editPhones? It will be fun!

You can also check thefull application in case you need to know how I've implemented these two mutations.

:octocat::octocat: I hope this guide have helped you!:octocat::octocat:

Releases

No releases published

Packages

No packages published

[8]ページ先頭

©2009-2025 Movatter.jp