{"id":44103173,"url":"https://github.com/conradwt/zero-to-graphql-using-elixir","last_synced_at":"2026-02-08T14:37:42.926Z","repository":{"id":37657311,"uuid":"140485080","full_name":"conradwt/zero-to-graphql-using-elixir","owner":"conradwt","description":"The purpose of this example is to provide details as to how one would go about using GraphQL with the Elixir Language.","archived":false,"fork":false,"pushed_at":"2026-01-25T08:02:03.000Z","size":12351,"stargazers_count":25,"open_issues_count":3,"forks_count":7,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-01-25T22:42:27.441Z","etag":null,"topics":["elixir","graphql","graphql-server","hacktoberfest","phoenix","phoenix-framework","tutorial","tutorial-code"],"latest_commit_sha":null,"homepage":"","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/conradwt.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2018-07-10T20:40:56.000Z","updated_at":"2026-01-25T08:01:57.000Z","dependencies_parsed_at":"2023-09-24T05:53:41.023Z","dependency_job_id":"c39b40d2-6d26-47fa-88df-8f280170e621","html_url":"https://github.com/conradwt/zero-to-graphql-using-elixir","commit_stats":null,"previous_names":[],"tags_count":128,"template":false,"template_full_name":null,"purl":"pkg:github/conradwt/zero-to-graphql-using-elixir","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/conradwt%2Fzero-to-graphql-using-elixir","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/conradwt%2Fzero-to-graphql-using-elixir/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/conradwt%2Fzero-to-graphql-using-elixir/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/conradwt%2Fzero-to-graphql-using-elixir/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/conradwt","download_url":"https://codeload.github.com/conradwt/zero-to-graphql-using-elixir/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/conradwt%2Fzero-to-graphql-using-elixir/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29233476,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-08T14:18:14.570Z","status":"ssl_error","status_checked_at":"2026-02-08T14:18:14.071Z","response_time":57,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["elixir","graphql","graphql-server","hacktoberfest","phoenix","phoenix-framework","tutorial","tutorial-code"],"created_at":"2026-02-08T14:37:42.858Z","updated_at":"2026-02-08T14:37:42.917Z","avatar_url":"https://github.com/conradwt.png","language":"Elixir","readme":"# Zero to GraphQL Using Elixir\n\nThe purpose of this example is to provide details as to how one would go about using GraphQL with the Elixir Language. Thus, I have created two major sections which should be self explanatory: Quick Installation and Tutorial Installation.\n\n## Getting Started\n\n## Software requirements\n\n- Elixir 1.19.5 or newer\n\n- Erlang 28.3 or newer\n\n- Phoenix 1.7.21 or newer\n\n- PostgreSQL 18.1 or newer\n\nNote: This tutorial was updated on macOS 26.2 (Tahoe).\n\n## Communication\n\n- If you **need help**, use [Stack Overflow](http://stackoverflow.com/questions/tagged/absinthe). (Tag 'absinthe')\n- If you'd like to **ask a general question**, use [Stack Overflow](http://stackoverflow.com/questions/tagged/absinthe).\n- If you **found a bug**, open an issue.\n- If you **have a feature request**, open an issue.\n- If you **want to contribute**, submit a pull request.\n\n## Quick Installation\n\n1.  clone this repository\n\n    ```zsh\n    git clone https://github.com/conradwt/zero-to-graphql-using-elixir.git\n    ```\n\n2.  change directory location\n\n    ```zsh\n    cd zero-to-graphql-using-elixir\n    ```\n\n3.  install and compile dependencies\n\n    ```zsh\n    mix do deps.get, deps.compile\n    ```\n\n4.  create, migrate, and seed the database\n\n    ```zsh\n    mix ecto.setup\n    ```\n\n5.  start the server\n\n    ```zsh\n    mix phx.server\n    ```\n\n6.  navigate to our application within the browser\n\n    ```zsh\n    open http://localhost:4000/graphiql\n    ```\n\n7.  enter the below GraphQL query on the left side of the browser window\n\n    ```graphql\n    {\n      person(id: 1) {\n        firstName\n        lastName\n        username\n        email\n        friends {\n          firstName\n          lastName\n          username\n          email\n        }\n      }\n    }\n    ```\n\n8.  run the GraphQL query\n\n    ```text\n    Control + Enter\n    ```\n\n    Note: The GraphQL query is responding with same response but different shape\n    within the GraphiQL browser because Elixir Maps perform no ordering on insertion.\n\n## Tutorial Installation\n\n1.  create the project\n\n    ```zsh\n    mix phx.new zero-to-graphql-using-elixir \\\n      --app zero_phoenix \\\n      --module ZeroPhoenix \\\n      --no-html \\\n      --no-assets\n    ```\n\n    Note: Just answer 'y' to all the prompts that appear.\n\n2.  switch to the project directory\n\n    ```zsh\n    cd zero-to-graphql-using-elixir\n    ```\n\n3.  update `username` and `password` database credentials which appears at the bottom of the following files:\n\n    ```text\n    config/dev.exs\n    config/test.exs\n    ```\n\n4.  create the database\n\n    ```zsh\n    mix ecto.create\n    ```\n\n5.  generate contexts, schemas, and migrations for the `Person` resource\n\n    ```zsh\n    mix phx.gen.context Accounts Person people first_name:string last_name:string username:string email:string\n    ```\n\n6.  replace the generated `Person` schema with the following:\n\n    `lib/zero_phoenix/account/person.ex`:\n\n    ```elixir\n    defmodule ZeroPhoenix.Accounts.Person do\n      use Ecto.Schema\n\n      import Ecto.Changeset\n\n      alias ZeroPhoenix.Accounts.Friendship\n      alias ZeroPhoenix.Accounts.Person\n\n      schema \"people\" do\n        field :email, :string\n        field :first_name, :string\n        field :last_name, :string\n        field :username, :string\n\n        has_many :friendships, Friendship\n        has_many :friends, through: [:friendships, :friend]\n\n        timestamps()\n      end\n\n      @doc false\n      def changeset(%Person{} = person, attrs) do\n        person\n        |\u003e cast(attrs, [:first_name, :last_name, :username, :email])\n        |\u003e validate_required([:first_name, :last_name, :username, :email])\n      end\n    end\n    ```\n\n7.  migrate the database\n\n    ```zsh\n    mix ecto.migrate\n    ```\n\n8.  generate contexts, schemas, and migrations for the `Friendship` resource\n\n    ```zsh\n    mix phx.gen.context Accounts Friendship friendships person_id:references:people friend_id:references:people\n    ```\n\n9.  replace the generated `CreateFriendship` migration with the following:\n\n    `priv/repo/migrations/\u003csome datetime\u003e_create_friendship.exs`:\n\n    ```elixir\n    defmodule ZeroPhoenix.Repo.Migrations.CreateFriendship do\n      use Ecto.Migration\n\n      def change do\n        create table(:friendships) do\n          add :person_id, references(:people, on_delete: :delete_all)\n          add :friend_id, references(:people, on_delete: :delete_all)\n\n          timestamps()\n        end\n\n        create index(:friendships, [:person_id])\n        create index(:friendships, [:friend_id])\n      end\n    end\n    ```\n\n10. replace the generated `Friendship` schema with the following:\n\n    `lib/zero_phoenix/accounts/friendship.ex`:\n\n    ```elixir\n    defmodule ZeroPhoenix.Accounts.Friendship do\n      use Ecto.Schema\n\n      import Ecto.Changeset\n\n      alias ZeroPhoenix.Accounts.Friendship\n      alias ZeroPhoenix.Accounts.Person\n\n      @required_fields [:person_id, :friend_id]\n\n      schema \"friendships\" do\n        belongs_to :person, Person\n        belongs_to :friend, Person\n\n        timestamps()\n      end\n\n      @doc false\n      def changeset(%Friendship{} = friendship, attrs) do\n        friendship\n        |\u003e cast(attrs, @required_fields)\n        |\u003e validate_required(@required_fields)\n      end\n    end\n    ```\n\n    Note: We want `friend_id` to reference the `people` table because our `friend_id` really represents a `Person` schema.\n\n11. migrate the database\n\n    ```zsh\n    mix ecto.migrate\n    ```\n\n12. create dev support directory\n\n    ```zsh\n    mkdir -p dev/support\n    ```\n\n13. update the search for compiler within `mix.exs`:\n\n    change:\n\n    ```elixir\n    defp elixirc_paths(:test), do: [\"lib\", \"test/support\"]\n    defp elixirc_paths(_), do: [\"lib\"]\n    ```\n\n    to:\n\n    ```elixir\n    defp elixirc_paths(:test), do: [\"lib\", \"dev/support\", \"test/support\"]\n    defp elixirc_paths(:dev), do: [\"lib\", \"dev/support\"]\n    defp elixirc_paths(_), do: [\"lib\"]\n    ```\n\n14. create `seeds.ex` support file with the following content:\n\n    `dev/support/seeds.ex`:\n\n    ```zsh\n    defmodule ZeroPhoenix.Seeds do\n      alias ZeroPhoenix.Accounts.{Person, Friendship}\n      alias ZeroPhoenix.Repo\n\n      def run() do\n        #\n        # reset database\n        #\n\n        reset()\n\n        #\n        # create people\n        #\n\n        me =\n          Repo.insert!(%Person{\n            first_name: \"Conrad\",\n            last_name: \"Taylor\",\n            email: \"conradwt@gmail.com\",\n            username: \"conradwt\"\n          })\n\n        dhh =\n          Repo.insert!(%Person{\n            first_name: \"David\",\n            last_name: \"Heinemeier Hansson\",\n            email: \"dhh@37signals.com\",\n            username: \"dhh\"\n          })\n\n        ezra =\n          Repo.insert!(%Person{\n            first_name: \"Ezra\",\n            last_name: \"Zygmuntowicz\",\n            email: \"ezra@merbivore.com\",\n            username: \"ezra\"\n          })\n\n        matz =\n          Repo.insert!(%Person{\n            first_name: \"Yukihiro\",\n            last_name: \"Matsumoto\",\n            email: \"matz@heroku.com\",\n            username: \"matz\"\n          })\n\n        #\n        # create friendships\n        #\n\n        me\n        |\u003e Ecto.build_assoc(:friendships)\n        |\u003e Friendship.changeset(%{friend_id: matz.id})\n        |\u003e Repo.insert()\n\n        dhh\n        |\u003e Ecto.build_assoc(:friendships)\n        |\u003e Friendship.changeset(%{friend_id: ezra.id})\n        |\u003e Repo.insert()\n\n        dhh\n        |\u003e Ecto.build_assoc(:friendships)\n        |\u003e Friendship.changeset(%{friend_id: matz.id})\n        |\u003e Repo.insert()\n\n        ezra\n        |\u003e Ecto.build_assoc(:friendships)\n        |\u003e Friendship.changeset(%{friend_id: dhh.id})\n        |\u003e Repo.insert()\n\n        ezra\n        |\u003e Ecto.build_assoc(:friendships)\n        |\u003e Friendship.changeset(%{friend_id: matz.id})\n        |\u003e Repo.insert()\n\n        matz\n        |\u003e Ecto.build_assoc(:friendships)\n        |\u003e Friendship.changeset(%{friend_id: me.id})\n        |\u003e Repo.insert()\n\n        matz\n        |\u003e Ecto.build_assoc(:friendships)\n        |\u003e Friendship.changeset(%{friend_id: ezra.id})\n        |\u003e Repo.insert()\n\n        matz\n        |\u003e Ecto.build_assoc(:friendships)\n        |\u003e Friendship.changeset(%{friend_id: dhh.id})\n        |\u003e Repo.insert()\n\n        :ok\n      end\n\n      def reset do\n        Person\n        |\u003e Repo.delete_all()\n      end\n    end\n    ```\n\n15. update the `seeds.exs` file with the following content:\n\n    `priv/repo/seeds.exs`:\n\n    ```elixir\n    ZeroPhoenix.Seeds.run()\n    ```\n\n16. seed the database\n\n    ```zsh\n    mix run priv/repo/seeds.exs\n    ```\n\n17. add `absinthe`, `absinthe_plug`, and `cors_plug` hex package dependencies as follows:\n\n    `mix.exs`:\n\n    ```elixir\n    defp deps do\n      [\n        {:phoenix, \"~\u003e 1.7.21\"},\n        {:phoenix_ecto, \"~\u003e 4.4.3\"},\n        {:ecto_sql, \"~\u003e 3.10.1\"},\n        {:postgrex, \"~\u003e 0.17.5\"},\n        {:phoenix_live_dashboard, \"~\u003e 0.7.2\"},\n        {:swoosh, \"~\u003e 1.11.6\"},\n        {:finch, \"~\u003e 0.16.0\"},\n        {:telemetry_metrics, \"~\u003e 0.6.2\"},\n        {:telemetry_poller, \"~\u003e 1.0.0\"},\n        {:gettext, \"~\u003e 0.22.3\"},\n        {:jason, \"~\u003e 1.4.4\"},\n        {:bandit, \"~\u003e 1.3.0\"},\n        {:absinthe, \"~\u003e 1.7.10\"},\n        {:absinthe_plug, \"~\u003e 1.5.9\"},\n        {:cors_plug, \"~\u003e 3.0.3\"}\n      ]\n    end\n    ```\n\n18. install and compile dependencies\n\n    ```zsh\n    mix do deps.get, deps.compile\n    ```\n\n19. configure `CORSPlug` by adding the following content:\n\n    `lib/zero_phoenix_web/endpoint.ex`:\n\n    ```elixir\n    plug CORSPlug, origin: [\"*\"]\n    ```\n\n    Note: The above code should be added right before the following line:\n\n    ```elixir\n    plug(ZeroPhoenixWeb.Router)\n    ```\n\n20. create the GraphQL directory structure\n\n    ```zsh\n    mkdir -p lib/zero_phoenix_web/graphql/{resolvers,schemas/{queries,mutations},types}\n    ```\n\n21. add the GraphQL schema which represents our entry point into our GraphQL structure:\n\n    `lib/zero_phoenix_web/graphql/schema.ex`:\n\n    ```elixir\n    defmodule ZeroPhoenixWeb.GraphQL.Schema do\n      use Absinthe.Schema\n\n      import_types(ZeroPhoenixWeb.GraphQL.Types.Person)\n\n      import_types(ZeroPhoenixWeb.GraphQL.Schemas.Queries.Person)\n\n      query do\n        import_fields(:person_queries)\n      end\n    end\n    ```\n\n22. add our Person type which will be performing queries against:\n\n    `lib/zero_phoenix_web/graphql/types/person.ex`:\n\n    ```elixir\n    defmodule ZeroPhoenixWeb.GraphQL.Types.Person do\n      use Absinthe.Schema.Notation\n\n      import Ecto\n\n      alias ZeroPhoenix.Repo\n\n      @desc \"a person\"\n      object :person do\n        @desc \"unique identifier for the person\"\n        field :id, non_null(:string)\n\n        @desc \"first name of a person\"\n        field :first_name, non_null(:string)\n\n        @desc \"last name of a person\"\n        field :last_name, non_null(:string)\n\n        @desc \"username of a person\"\n        field :username, non_null(:string)\n\n        @desc \"email of a person\"\n        field :email, non_null(:string)\n\n        @desc \"a list of friends for our person\"\n        field :friends, list_of(:person) do\n          resolve fn _, %{source: person} -\u003e\n            {:ok, Repo.all(assoc(person, :friends))}\n          end\n        end\n      end\n    end\n    ```\n\n23. add the `person_queries` object to contain all the queries for a person:\n\n    `lib/zero_phoenix_web/graphql/schemas/queries/person.ex`:\n\n    ```elixir\n    defmodule ZeroPhoenixWeb.GraphQL.Schemas.Queries.Person do\n      use Absinthe.Schema.Notation\n\n      object :person_queries do\n        field :person, type: :person do\n          arg :id, non_null(:id)\n\n          resolve(\u0026ZeroPhoenixWeb.GraphQL.Resolvers.PersonResolver.find/3)\n        end\n      end\n    end\n    ```\n\n24. add the `PersonResolver` to fetch the individual fields of our person object:\n\n    `lib/zero_phoenix_web/graphql/resolvers/person_resolver.ex`:\n\n    ```elixir\n    defmodule ZeroPhoenixWeb.GraphQL.Resolvers.PersonResolver do\n      alias ZeroPhoenix.Accounts\n      alias ZeroPhoenix.Accounts.Person\n\n      def find(_parent, %{id: id}, _info) do\n        case Accounts.get_person(id) do\n          %Person{} = person -\u003e\n            {:ok, person}\n\n          _error -\u003e\n            {:error, \"Person id #{id} not found\"}\n        end\n      end\n    end\n    ```\n\n25. add routes for our GraphQL API and GraphiQL browser endpoints:\n\n    `lib/zero_phoenix_web/router.ex`:\n\n    replace\n\n    ```elixir\n    scope \"/api\", ZeroPhoenixWeb do\n      pipe_through :api\n    end\n    ```\n\n    with\n\n    ```elixir\n    scope \"/\" do\n      pipe_through :api\n\n      if Mix.env() in [:dev, :test] do\n        forward \"/graphiql\",\n          Absinthe.Plug.GraphiQL,\n          schema: ZeroPhoenixWeb.GraphQL.Schema,\n          json_codec: Jason,\n          interface: :playground\n      end\n\n      forward \"/graphql\",\n        Absinthe.Plug,\n        schema: ZeroPhoenixWeb.GraphQL.Schema\n    end\n    ```\n\n26. start the server\n\n    ```zsh\n    mix phx.server\n    ```\n\n27. navigate to our application within the browser\n\n    ```zsh\n    open http://localhost:4000/graphiql\n    ```\n\n28. enter the below GraphQL query on the left side of the browser window\n\n    ```graphql\n    {\n      person(id: 1) {\n        firstName\n        lastName\n        username\n        email\n        friends {\n          firstName\n          lastName\n          username\n          email\n        }\n      }\n    }\n    ```\n\n29. run the GraphQL query\n\n    ```text\n    Control + Enter\n    ```\n\n    Note: The GraphQL query is responding with same response but different shape\n    within the GraphiQL browser because Elixir Maps perform no ordering on insertion.\n\n## Production Setup\n\nReady to run in production? Please [check our deployment guides](http://www.phoenixframework.org/docs/deployment).\n\n## Phoenix References\n\n- Official website: http://www.phoenixframework.org/\n- Guides: http://phoenixframework.org/docs/overview\n- Docs: https://hexdocs.pm/phoenix\n- Mailing list: http://groups.google.com/group/phoenix-talk\n- Source: https://github.com/phoenixframework/phoenix\n\n## GraphQL References\n\n- Official Website: http://graphql.org\n- Absinthe GraphQL Elixir: http://absinthe-graphql.org\n\n## Support\n\nBug reports and feature requests can be filed with the rest for the Phoenix project here:\n\n- [File Bug Reports and Features](https://github.com/conradwt/zero-to-graphql-using-elixir/issues)\n\n## License\n\nZero to GraphQL Using Elixir is released under the [MIT license](./LICENSE.md).\n\n## Copyright\n\nCopyright \u0026copy; 2018 - 2026 Conrad Taylor. All rights reserved.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fconradwt%2Fzero-to-graphql-using-elixir","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fconradwt%2Fzero-to-graphql-using-elixir","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fconradwt%2Fzero-to-graphql-using-elixir/lists"}