developess / GraphQL-Workshop

A repo to help you get up to speed with implementing a graphql API in elixir!

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

GraphQL Workshop ๐ŸŒˆ

Disclaimer: this is an almost identical copy of Adzz's ex_ample repo, but has been specially adapted to be shorter and accessible to those new to Elixir and GraphQL.

The idea is to step through the branches completing the exercises outlined in the readme as you go. Answers can be found at the top of each branch's README for the preceeding section. You'll work through the branches in this order:

master
my-first-query
my-second-query
my-first-mutation
my-first-resolving-function

Background information ๐Ÿ–ผ

Note, these are NOT instructions - you don't need to do anything outlined in this section, but please read to understand the structure of the repo

Umbrella projects โ˜‚๏ธ

This repo is an 'umbrella' project. Umbrella projects are a great way to manage internal dependencies for your applications. Internal dependencies can be thought of as libraries that can sit on their own - but that you don't want to or cannot open source. They are things that you can configure their own releases for (so can be released independently from the rest of the application), but are conveniently grouped together into one git repo.

When is an umbrella project a good idea?
If you have ever had one repo rely on another, you'll soon find umbrella projects to be lifesavers; no more using git tags and bumping versions in your mix files so you can get new features!

However, apps within an umbrella projects are not completely decoupled. From the docs

While it provides a degree of separation between applications, those applications are not fully decoupled, as they are assumed to share the same configuration and the same dependencies.

And

If you find yourself in a position where you want to use different configurations in each application for the same dependency or use different dependency versions, then it is likely your codebase has grown beyond what umbrellas can provide.

Creating an umbrella project โ›ฑ

So far to create this repo, we first ran this command (NO NEED TO RUN THIS, just explaining how we got here):

mix new ex_ample --umbrella

The name of the app is ex_ample, and the umbrella flag does exactly what you think it does.

Adding to the umbrella โ˜”๏ธ

We added new individual apps to the umbrella project by running a command like this (from the root of the project):

cd apps && mix new app_name --sup

The sup flag stands for supervision, it just tells Mix to generate a supervision tree automatically for us, instead of having to build one manually. More in the docs ๐Ÿ‘ฉโ€โš•๏ธ

We have added an app called ex_ample_backend. This will act as the datasource for our application. It has VERY limited capabilities. I don't recommend that you read the code unless you have a penchant for punishment. I certainly don't recomend you use it past this exercise.

Adding a phoenix app ๐Ÿฆœ

We have (already!!) created a phoenix (web server) app called graphql using a command similar to this one:

cd apps && mix phx.new name_of_app --no-ecto --no-dashboard --no-live --no-mailer --no-assets

--no-ecto and other flags are optional. The --no-ecto flag means Ecto files are not generated (Ecto is a database wrapper for Elixir and as this example has no database, we don't need it.)

Wait, what is Phoenix?
Phoenix is a web development framework written in Elixir which (by default) implements the server-side Model View Controller (MVC) pattern. Check out the docs here: https://hexdocs.pm/phoenix/overview.html#content.

Adding dependencies ๐Ÿน

We wanted to add 2 dependencies to this project:

What is Absinthe? Why are we adding it?
Absinthe is the GraphQL toolkit for Elixir, built to suit Elixir's capabilities and style. With Absinthe, you define the schema and resolution functions and it executes GraphQL documents.

On client side Absinthe has support for Relay and Apollo client and in Elixir it uses Plug and Phoenix to support HTTP APIs, via absinthe_plug and absinthe_phoenix packages. It also has support for Ecto via the absinthe_ecto package.

Adding dependencies in elixir doesn't work like it does in javacript (npm install jest etc) - there are no magic words to install! We have added 2 dependencies manually in the mix.exs file inside the graphql app:

{:absinthe, "~> 1.4.0"},
{:absinthe_plug, "~> 1.4.0"},

So the dependency section now looks like this:

...
  # Specifies your project dependencies.
  #
  # Type `mix help deps` for examples and options.
  defp deps do
    [
      {:absinthe_plug, "~> 1.4.0"},
      {:absinthe, "~> 1.4.0"},
      {:cors_plug, "~> 1.5"},
      {:ex_ample_backend, in_umbrella: true},
      {:floki, ">= 0.30.0", only: :test},
      {:gettext, "~> 0.18"},
      {:jason, "~> 1.2"},
      {:phoenix_html, "~> 3.0"},
      {:phoenix_live_reload, "~> 1.2", only: :dev},
      {:phoenix_live_view, "~> 0.16.0"},
      {:phoenix, "~> 1.6.2"},
      {:plug_cowboy, "~> 2.5"},
      {:telemetry_metrics, "~> 0.6"},
      {:telemetry_poller, "~> 1.0"}
    ]
  end
end

Adding web server routes ๐Ÿ—บ

Inside our router.ex file in the graphql app, we've added 2 new routes. One is the route that Absinthe and Graphql use to host our GraphQL api (/graphql), the other is the route that the Graphiql tool uses (/graphiql), which is only available in development.

  scope "/graphql" do
      forward(
        "/",
        Absinthe.Plug,
        schema: Graphql.Schema,
        json_codec: Jason
      )
    end

  if Mix.env() == :dev do
    forward(
      "/graphiql",
      Absinthe.Plug.GraphiQL,
      schema: Graphql.Schema,
      json_codec: Jason,
      default_url: "/graphiql",
      interface: :advanced
    )
  end

You don't need to worry too much about the syntax here, or remember it, its just for information!

Adding a schema file and some smoke tests ๐Ÿ’จ

We have added a schema.ex file and add a resolvers folder with a resolver.ex file in it.

Inside schema.ex we have defined the schema module, imported Absinthe, and written two 'hello world' smoke tests so we can check our api and resolvers are all working.

defmodule Graphql.Schema do
  # This allows us to use the absinthe schema notation like 'query' and 'field'
  use Absinthe.Schema

  query do
    field :is_this_thing_on, type: :string do
      resolve(&Graphql.Resolver.smoke_test/2)
    end
  end

  mutation do
    field :echo_text, type: :string do
      arg(:input, :string)
      resolve(&Graphql.Resolver.test_update/2)
    end
  end
end

Inside resolvers.ex we added the two resolver functions from the schema.

defmodule Graphql.Resolver do
  def smoke_test(_args, _info) do
    {:ok, "Yes!"}
  end

  def test_update(%{input: input}, _info) do
    {:ok, input}
  end
end

Try and understand what's happening in these two files, as you will be using the same syntax and concepts to write your own queries shortly! Speak to another student or a workshop leader if you have questions.

Side point: note how the Absinthe resolve function (in the schema) expects resolver functions to return an "ok tuple" (or an "error tuple" in the case of an error), which is a tuple containing an ok or error atom and some data. This is a common way in elixir to handle error catching and propagation.

# ok tuple (response is a variable that would contain some data)
{:ok, reponse}

# error tuple
{:error, response}

Workshop Instructions

  1. Clone this repo:
git clone https://github.com/developess/GraphQL-Workshop
  1. Install Phoenix web framework if you don't have it already. You can find instructions here:

  2. Fetch the dependencies for the repo by running: mix deps.get

  3. Get your Phoenix server running: mix phx.server (don't worry about any deprecation warnings)

If you go to localhost:4000/graphiql you should be able to see the Graphiql interface with the smoke tests above in the docs panel! Try writing the query and/or mutation for one of the existing fields defined in the schema (the smoke tests).

Then, checkout the next branch: my-first-query for your first challenge.

About

A repo to help you get up to speed with implementing a graphql API in elixir!


Languages

Language:Elixir 100.0%