CRUD: GraphQL with Elixir/Phoenix

I’m currently studying GraphQL with Elixir/Phoenix in preparation for this web and mobile app project. Although it’s easy to find tutorials on different sites, I’ve noticed that there’s no source discussing the CRUD process as a whole, so I’ve decided to do this article to combine each process altogether. Hope it helps.

GraphQL Schema

Objects

First of all, we need to define the object and input_object, we will use both of them in most of our queries and mutations.

# define your resolveralias ApiWeb.{
TagResolver
}
@desc “Tag data”
object :tag do
field :id, non_null(:id)
field :name, :string
end
# this input_object holds the parameter/s for the update method in our mutation input_object :tag_params do
field(:name, :string)
end

Queries

For the query part, we will define 2 queries: one that gets all the tags and another that gets the tag by id (the read part in crud).

# Tag queriesquery do   field :tags, non_null(list_of(non_null(:tag))) do
arg :all, :boolean
resolve &TagResolver.all/3
end
field :tag, type: :tag do
arg :id, non_null(:id)
resolve &TagResolver.get/2
end
end

Mutations

After the query, we will now start writing our mutations for create, update, and delete tag.

mutation do   field :create_tag, type: :tag do
arg(:name, non_null(:string))
resolve(&TagResolver.create/2)
end
field :update_tag, type: :tag do
arg(:id, non_null(:id))
arg(:post, :tag_params)
resolve(&TagResolver.update/2)
end
field :delete_tag, type: :tag do
arg(:id, non_null(:id))
resolve(&TagResolver.delete/2)
end
end

GraphQL Resolver

After writing queries and mutations, let’s go ahead and write our resolvers for each of them in consecutive order.

defmodule ApiWeb.TagResolver do # Define your elixir context 
alias Data.Contexts.TagContext, as: TC
def all(_root, _args, _info) do
{:ok, TC.all_tags}
end
def get(%{id: id}, _info) do
with {:ok, info} <- UUID.info(id),
tag <- TC.get(id)
do
{:ok, tag}
else
{:error, error} -> {:error, "No tag found"}
end
end
def create(tag_params, _info) do
with {:ok, tag} <- TC.insert(tag_params) do
{:ok, tag}
else
{:error, error} -> {:error, "Failed to create tag"}
end
end
def update(%{id: id, post: tag_params}, _info) do
case get(%{id: id}, _info) do
{:ok, post} -> post |> TC.modify(tag_params)
{:error, _} -> {:error, "Failed to update tag"}
end
end
def delete(%{id: id}, _info) do
with {:ok, post} <- TC.delete(id) do
{:ok, post}
else
{:error, _} -> {:error, "Failed to delete tag"}
end
end
end

Elixir Context

After the resolver, the last thing we need to do is to create the context methods we used in our resolvers.

defmodule Data.Contexts.TagContext do   alias Data.Repo
alias Data.Schemas.Tag
import Ecto.Query
def all_tags() do
Tag
|> Repo.all
end
def get(id) do
Tag
|> Repo.get(id)
end
def insert(params) do
Tag.changeset(%Tag{}, params)
|> Repo.insert
end
def modify(struct, params) do
Tag.changeset(struct, params)
|> Repo.update
end

def delete(id) do
tag = Tag |> Repo.get(id)
Repo.delete(tag)
end
end

results in graphiql:

all_tags and tag_by_id read method
create tag mutation method
update tag mutation method
delete tag mutation method

Elixir Developer — www.alvinrapada.com

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store