Creating GraphQL APIs using Elixir Phoenix and Absinthe

GraphQL is a new hype in the Field of API technologies. We have been constructing and using REST API's for quite some time now and started hearing about GraphQL recently. GraphQL is usually described as a frontend-directed API technology as it allows front-end developers to request data in a more simpler way than ever before. The objective of this query language is to formulate client applications formed on an instinctive and adjustable format, for portraying their data prerequisites as well as interactions.

The Phoenix Framework is running on Elixir, which is built on top of Erlang. Elixir core strength is scaling and concurrency. Phoenix is a powerful and productive web framework that does not compromise speed and maintainability. Phoenix comes in with built-in support for web sockets, enabling you to build real-time apps.

Prerequisites:

  1. Elixir & Erlang: Phoenix is built on top of these

  2. Phoenix Web Framework: For writing the web application

  3. Absinthe: GraphQL toolkit for Elixir for writing queries/mutations

  4. GraphiQL: An in-browser IDE for exploring GraphQL. You can think of it as a postman for REST. We will use the mac app from here which is a wrapper around GraphiQL.

Overview:

The application we will be developing is a simple blog application written using Phoenix Framework with two schemas User and Post defined in Accounts and Blog resp. We will design the application to support API's related to blog creation and management. Assuming you have Erlang, Elixir and mix installed.

Where to Start:

At first, we have to create a Phoenix web application using the following command:

mix phx.new  --no-brunch --no-html

--no-brunch - do not generate brunch files for static asset building. When choosing this option, you will need to manually handle JavaScript  dependencies if building HTML apps

• --no-html - do not generate HTML views.

Note: As we are going to mostly work with API, we don't need any web pages, HTML views and so the command args and

Dependencies:

After we create the project, we need to add dependencies in mix.exs to make GraphQL available for the Phoenix application.

defp deps do
  [
   {:absinthe, "~> 1.3.1"},
   {:absinthe_plug, "~> 1.3.0"},
   {:absinthe_ecto, "~> 0.1.3"}
  ]
end

Structuring the Application:

We need a few things to structure the GraphQL part of the application :

  1. GraphQL Schemas : placed in lib/graphql_web/schema/schema.ex folder

  2. Custom types: We have to define some custom types to be used in schema placed in lib/graphql_web/schema/types.ex

  3. Resolvers: Schemas map each query or mutation to a resolver function to handle the business logic. Resolvers are placed in their own files. In our case, in lib/graphql/accounts/user_resolver.ex and lib/graphql/blog/post_resolver.ex folder.

Also, we need to modify the router to be able to make queries using the GraphQL client in lib/graphql_web/router.ex and also have to create a GraphQL pipeline to route the API request which also goes inside lib/graphql_web/router.ex :

Writing GraphQL Queries:

Lets write some graphql queries which can be considered too be equivalent to GET requests in REST. But before getting into queries lets take a look at GraphQL schema we defined and its equivalent resolver mapping :

You can see above we have defined four queries in the schema. Lets pick a query and see what goes into it :

field :accounts_user, :accounts_user do
  arg(:email, non_null(:string))
  resolve(&Graphql.Accounts.UserResolver.find/2)
end

Here, we have defined a query to retrieve a particular user using his email address.

  1. arg(:, ): is the incoming argument which is email and its have to be a non-null string

  2. Graphql.Accounts.UserResolver.find/2 : is the resolver function that will have the core logic of retrieving a user.

  3. Accounts_user : is our custom defined resolved type. It’s defined in lib/graphql_web/schema/types.ex as follows —

object :accounts_user do
  field(:id, :id)
  field(:name, :string)
  field(:email, :string)
  field(:posts, list_of(:blog_post), resolve: assoc(:blog_posts))
end

Once the queries are defined, we need to write the resolver functions. Will go over the resolver function for accounts_user which is present in lib/graphql/accounts/user_resolver.ex file :

This function is used to list all users or retrieve a particular user using an email address. Let’s run it now using GraphiQL browser. You need to have the server running on port 4000. To start the Phoenix server use :-

mix deps.get     #pulls all the dependencies
mix deps.compile #compile your code
mix phx.server   #starts the phoenix server

Let’s run a query to retrieve a user using email address :

newblog_1.png

Here, we are running accountsUser query with an email address to retrieve the id, email, and name fields. GraphQL also supports defining variables which we will show later when writing mutations.

Let’s run another query to list all blog posts:

 Writing GraphQL Mutations :

Let's write some GraphQl mutations. If you have understood the way graphql queries are written mutations are much simpler and similar to queries and easy to understand. It is defined in the same form as queries with a resolver function. Different mutations we are gonna write are as follow:

  1. create_post:- create a new blog post

  2. update_post :- update a existing blog post

  3. delete_post:- delete an existing blog post

The mutation looks as follows:

Let's run some mutations to create a post in GraphQL:

newblog_3.png

Notice the method is POST and not GET over here.

Let's dig into update mutation function :

field :update_post, type: :blog_post do
  arg(:id, non_null(:id))
  arg(:post, :update_post_params)

  resolve(&Graphql.Blog.PostResolver.update/2)
end

Here, update post takes two arguments as input ,  non null id and a post parameter of type update_post_params that holds the input parameter values to update. The mutation is defined in lib/graphql_web/schema/schema.ex while the input parameter values are defined in lib/graphql_web/schema/types.ex —

input_object :update_post_params do
 field(:title, :string)
 field(:body, :string)
 field(:accounts_user_id, :id)
end

The difference with previous type definitions is that it’s defined as input_object instead of object.

The corresponding resolver function is defined as follows :

def update(%{id: id, post: post_params}, _info) do
  case find(%{id: id}, _info) do
    {:ok, post} -> post |> Blog.update_post(post_params)
    {:error, _} -> {:error, "Post id #{id} not found"}
  end
end
newblog_4.png

Here we have defined a query parameter to specify the id of the blog post to be updated.

CONCLUSION

This is all you need, to write a basic GraphQL server for any Phoenix application using Absinthe.

References:

  1. https://www.howtographql.com/graphql-elixir/0-introduction/

  2. https://pragprog.com/book/wwgraphql/craft-graphql-apis-in-elixir-with-absinthe

  3. https://itnext.io/graphql-with-elixir-phoenix-and-absinthe-6b0ffd260094

5019916.jpeg

Pratik is a Senior Software Engineer at Velotio. He has worked on various technology platforms like Elixir, Clojure, and Python. He has deep expertise in the Adtech space. In free time he likes to play football, FIFA on the Xbox and watch Netflix. He is an aspiring trekker.