in Elixir, GraphQL, Phoenix

Phoenix GraphQL Tutorial with Absinthe: Add CRUD Using Mutations

GraphQL Create Read Update Delete

This is the second post in a series on using Phoenix and GraphQL to create clean and powerful API’s. In this post I will cover mutations and how they can be used to add CRUD functionality to an app. For those who are just joining us, I recommend going through the last post before starting this one. However, you can start where we left off last time by cloning the repo and checking out the checkpoint-1 branch. If you go that route you’ll want to mix run priv/repo/seeds.exs  in order to populate your database with some dummy data after you clone the repo and create your database (postgres).

Preparing the Posts Model

We will be adding CRUD functionality to our post model and will need to make some changes to ensure that everything works properly. Currently, the post changeset doesn’t allow any other fields besides :title  and :body . Let’s add :user_id  to the cast  and validate_required  functions so we can properly support associations between users and posts. Our new changeset should look like this:

Now we should be able to accept a :user_id .

Mutations

In GraphQL, there are only two types of operations: Queries and Mutations. A mutation is an operation that “mutates” the underlying data system. Mutations are how you create, read, update, or delete (CRUD) data. You can also use a mutation for something like a login operation in which you send an email and password to the server in order to generate a JWT to send back to the user (I’ll cover that in a future post). For now, we will go through each of the parts in CRUD to demonstrate how this is done in GraphQL.

Create

Our post changeset requires that we pass it 3 arguments: :title , :body , and :user_id . As a result, we will need to create a mutation that requires these 3 arguments. Open up your web/schema.ex  file and add the following code:

As you can see, we’ve added a new mutation block to the bottom of the module. Inside this block we define a :create_post  mutation. It accepts each of the arguments we need and makes them required by using the non_null  helper. This mutation block is where all of your mutations will go. Now, you may be thinking, “won’t this get super gnarly if we have a lot of mutations?” The answer is yes. Fortunately, Absinthe provides us with some helper functions to clean up our code. I’ll be covering them in more detail in a future post but for the curious you can check them out here. Also, for some examples of how import_types  and import_fields  work you can see them in action in this test file.

Let’s now define MyApp.PostResolver.create/2  to get this mutation working:

With that change we should now be able to create new posts. Open up GraphiQL at http://localhost:4000/graphiql  and lets try adding a post. Run the following operation:

It works! Congratulations, you’ve added your first mutation. One thing to note here is that we can ask the mutation to query data about the new post after it’s been created. In this example we are asking the mutation to give us the new :id .

We can skip the R in CRUD since we’ve already covered querying for data so let’s move on to adding update functionality.

Update

Before we can implement update functionality I need to introduce a new concept. GraphQL mutation arguments can be one of a few types of data (the full list). One of those types is the input_object . It is similar to the other GraphQL Objects that we’ve made previously but it represents an object of data that is passed to a mutation as an argument. Let’s create one to represent the  post_params for updating a post and then we will define the :update_post  mutation:

Our :update_post  mutation requires two arguments: a post :id  and data to update a post in the form of the :post  argument. Let’s now add the MyApp.PostResolver.update/2  function:

If we jump back into GraphiQL we can run the following operation to see this in action:

With that we now have CRU! Let’s finish it off by adding delete functionality.

Delete

Our last task is to add the ability to delete posts. We can do this with a delete mutation. Let’s add the following code to web/schema.ex :

Our :delete_post  mutation will accept one argument: a post :id.  Let’s now add the MyApp.PostResolver.delete/2  function:

Let’s test it out. Go back into GraphiQL and run the following operation:

It works! Now we can delete posts.

Conclusion

In this post we built off of the last post and added CRUD functionality to our API. As you can see, it’s relatively painless to start adding mutations to a GraphQL API in Phoenix with the excellent Absinthe library.

I’ve added the code from this tutorial to the repo and it will be under the branch checkpoint-2. As always, if you have any questions feel free to reach out. I’m happy to help in any way that I can.

You can find the next post in the series here.