Craft GraphQL APIs in Elixir with Absinthe (Pragprog)

Just picked this up - thanks for the forum discount! New to elixir, and in-between jobs right now so it really helps!

Take care, dave

2 Likes

Hey everyone! The book is officially out! https://pragprog.com/book/wwgraphql/craft-graphql-apis-in-elixir-with-absinthe

Many thanks to our reviewers, beta-readers, and Absinthe contributors!

17 Likes

Congrats!!!

5 Likes

Is there an updated version of the code which accompanies the book anywhere?

Running the initial mix ecto.setup results in a load of deprecation warnings but fails with this:

== Compilation error in file lib/plate_slate_web/schema.ex ==
** (Absinthe.Schema.Error) Invalid schema:
Elixir.PlateSlateWeb.Schema:0: The root query type must be implemented and be a of type Object

Iā€™m guessing that having installed Phoenix 1.4 isnā€™t going to help much either however.

Would it be simpler for me to recreate the initial project from an empty one and add in up-to-date deps and bear that in mind as I work through the book?

What version of elixir are you on? Does your schema have a root query object? If so, upgrade Absinthe to the latest version.

Thanks Ben. Iā€™m on Elixir 1.7.3.

Iā€™m using the code from the PragProg site (in the 02-chp.schema/1-start folder).

Iā€™ve just commented out the contents of the PlateSlateWeb.Schema module and then mix ecto.setup works and generates the database.

Simply uncommenting use Absinthe.Schema causes the error I mentioned earlier.

At the bottom of the error output is:

lib/absinthe/schema.ex:258: Absinthe.Schema.__after_compile__/2
(stdlib) lists.erl:1263: :lists.foldl/3
(stdlib) erl_eval.erl:680: :erl_eval.do_apply/6
(elixir) lib/kernel/parallel_compiler.ex:206: anonymous fn/4 in Kernel.ParallelCompiler.spawn_workers/6

I will have more of a dig into it later but Iā€™ve started a new project and am slowly building it up using a combination of the book, your blog post (https://www.howtographql.com/graphql-elixir/0-introduction/) and the Absinthe documentation.

As I say, Iā€™m pretty sure itā€™s an Elixir or Phoenix update issue because I did work through the first chapters of the book a while ago without this problem.

Iā€™ll let you know if I get to the bottom of it.

Yeah just upgrade Absinthe, Elixir 1.7 fixed a bug that Absinthe was apparently relying on during schema generation. We fixed it a while ago but the book code still ships with relatively older versions of Absinthe since, well, itā€™s hard to change print.

2 Likes

Thanks Ben, that did the trick (as long as I left everything else alone). There are still a lot of deprecation warnings but I completely understand about the book being published at a fixed point in time.

Fingers crossed that one day you and Bruce have the willpower to work on a revised edition. Now that Iā€™ve started tinkering with it, GraphQL and Absinthe are fantastic. Thank you for all of your hard work, itā€™s very much appreciated, even by those of us who might not have time to contribute to the project (but maybe I will as I get more into using it).

1 Like

This book mentions that we should use the following versions of technologies used,
ā€¢ Erlang (v20.0+)
ā€¢ Elixir (v1.5.0+)
ā€¢ PostgreSQL (v9.4+)

But when I peeked into the code it says,

def project do
    [
      ...
      elixir: "~> 1.4",
      ...
  end

which means that itā€™s using Elixir version 1.4 or above and 1.4 depends on Erlang v18/v19 (Erlang v20 from Elixir v1.4.5).

So what versions of Erlang and Elixir should I use?
A month or two months ago I tried to follow this book and failed because some versions issue, so this time I want to be sure that Iā€™m using the correct versions.
@benwilson512

@pillaiindu Use the versions mentioned in the book. The code was first generated in Elixir 1.4 and we simply forgot to update that one spot.

Should I change that line from elixir: "~> 1.4", to elixir: "~> 1.5" after copying the code?

It wonā€™t make any difference. That line is only used to validate that youā€™re running the project in at least Elixir 1.4, it doesnā€™t choose the elixir version.

Thank you for your time! :slight_smile:

1 Like

Thanks for using Absinthe!

1 Like

So what versions of Erlang and Elixir should I use?

Iā€™ve reread the book recently and worked through the examples with elixir 1.8.1 and latest absinthe, dataloader, phoenix, ecto, etc. Donā€™t remember encountering any major problems, so Iā€™d say the latest versions of everything should work.

2 Likes

This is also correct. The main thing that has tripped people up in the past is that they have tried to use the latest Elixir / Erlang without upgrading Absinthe. This leads to compilation errors. If however you upgrade Absinthe and other dependencies, it should all be smooth sailing.

5 Likes

I just got the P2 version.

Thanks for updating :slight_smile:

1 Like

Iā€™m excited to read P2 before the start of my next large project!

1 Like

Has anyone found a nice way of doing the challenge at the end of chapter 7 (Resolution Middleware)?

Iā€™ve been trying to do it today and Iā€™m getting nowhere. I think I donā€™t understand where the time taken is meant to be stored? Should it be on the response?

JFYI the challenge is to ā€œBuild some middleware that can be used to measure how long it takes to resolve a fieldā€.

I know Iā€™m showing lack of effort here but I can only do Elixir in my spare time (Python/JS dev by day) so any guidance or hints would be greatly appreciated.

To answer my own question, today I started chapter 9 and it does exactly this thing with a ā€œbookendedā€ middleware using :start and :finish calls. So anyone else doing this, keep reading and all will become clear.

To elaborate, I just expanded the Debug middleware from chapter 9 like this:

defmodule PlateSlateWeb.Schema.Middleware.Debug do
  @behaviour Absinthe.Middleware

  def call(resolution, :start) do
    path = resolution |> Absinthe.Resolution.path |> Enum.join(".")
    time1 = :erlang.timestamp
    IO.puts """
    ========START==========
    started: #{path}
    with source: #{inspect resolution.source}
    """
    %{resolution | middleware: resolution.middleware ++ [{__MODULE__, {:finish, path, time1}}]}
  end

  def call(resolution, {:finish, path, time1}) do
    time2 = :erlang.timestamp
    time_taken = :timer.now_diff(time2, time1)
    IO.puts """
    completed: #{path}
    time taken: #{time_taken} microseconds (#{:erlang.float_to_binary(time_taken * 1.0e-6, [{:decimals, 6}])} seconds )
    value: #{inspect resolution.value}
    ========END==========\
    """
    resolution
  end
end

Itā€™s probably not the best solution but it works for me!

1 Like