Skip to main content

Absinthe GraphQL Toolkit

Absinthe is a powerful GraphQL toolkit for Elixir, a functional programming language built on top of the Erlang virtual machine.

It allows developers to build flexible and efficient APIs by leveraging the GraphQL query language. In this tutorial, we will explore the history, features, and several examples of Absinthe.

History of Absinthe

Absinthe was created by Ben Wilson and Bruce Williams in 2016 as an open-source project. It was developed to provide Elixir developers with a seamless way to build GraphQL APIs. Since its release, Absinthe has gained popularity within the Elixir community and has become the go-to framework for GraphQL development in Elixir.

Features of Absinthe

Absinthe offers a wide range of features that make it a powerful tool for building GraphQL APIs:

  1. Schema Definition Language (SDL): Absinthe provides a clean and concise syntax for defining your GraphQL schema using SDL. SDL allows you to define types, queries, mutations, and subscriptions in a human-readable format.

    defmodule MyApp.Schema do
    use Absinthe.Schema

    schema """
    type Query {
    hello: String
    }
    """
    end

    In the above example, we define a simple schema with a single query field hello that returns a string.

  2. Resolvers: Absinthe allows you to define resolvers for your schema fields. Resolvers are responsible for fetching the data for a particular field. You can define resolvers using plain Elixir functions or by using the Absinthe.Resolution macro.

    defmodule MyApp.Schema do
    use Absinthe.Schema

    schema """
    type Query {
    hello: String
    }
    """

    def hello(_, _) do
    "Hello, World!"
    end
    end

    In the above example, we define a resolver for the hello query field that returns the string "Hello, World!".

  3. Input Objects: Absinthe supports input objects, which allow you to pass complex arguments to your queries and mutations. Input objects can have multiple fields with different types.

    input_object :user_input do
    field :name, non_null(:string)
    field :age, :integer
    end

    In the above example, we define an input object user_input with two fields: name of type String! (non-null string) and age of type Integer.

  4. Middleware: Absinthe provides a middleware system that allows you to customize the behavior of your GraphQL API. Middleware functions can be used for authentication, authorization, logging, and more.

    defmodule MyApp.AuthenticationMiddleware do
    def call(resolution, _) do
    # Perform authentication logic here
    resolution
    end
    end

    In the above example, we define a simple authentication middleware that performs authentication logic before resolving the GraphQL query.

  5. Subscriptions: Absinthe supports real-time updates through GraphQL subscriptions. Subscriptions allow clients to subscribe to specific events and receive updates when those events occur.

    subscription :newMessageAdded, :message do
    # Subscription resolution logic
    end

    In the above example, we define a subscription newMessageAdded that resolves to a message object.

Examples of Absinthe

Now let's explore some practical examples of Absinthe to better understand its features.

  1. Example 1: Getting Started

    To get started with Absinthe, you need to add the absinthe and absinthe_plug dependencies to your project. Then, you can define your schema and mount it as a plug in your Phoenix application.

    # mix.exs
    defp deps do
    [
    {:absinthe, "~> 1.6"},
    {:absinthe_plug, "~> 1.6"}
    ]
    end
    # lib/my_app_web/router.ex
    pipeline :api do
    plug :accepts, ["json"]
    end

    scope "/api" do
    pipe_through :api

    forward "/graphql", Absinthe.Plug,
    schema: MyApp.Schema
    end

    In the above example, we define a basic setup for integrating Absinthe with a Phoenix application.

  2. Example 2: Querying Data

    Let's define a schema with a query field to fetch a list of users from a database.

    schema """
    type Query {
    users: [User!]
    }

    type User {
    id: ID!
    name: String!
    email: String!
    }
    """

    Now, we can define a resolver function to fetch the list of users from the database.

    def users(_, _) do
    users = MyApp.Repo.all(MyApp.User)
    {:ok, users}
    end

    In the above example, the resolver function users fetches all the users from the database using Ecto (Elixir's database wrapper). The fetched users are then returned as a list.

  3. Example 3: Mutations

    We can also define mutations to create, update, or delete data. Let's define a mutation to create a new user.

    schema """
    type Mutation {
    createUser(input: UserInput!): User!
    }

    input UserInput {
    name: String!
    email: String!
    }
    """

    Now, we can define a resolver function to handle the createUser mutation.

    def create_user(_, %{input: %{"name" => name, "email" => email}}) do
    user = MyApp.Repo.insert(MyApp.User.changeset(%MyApp.User{}, %{name: name, email: email}))
    {:ok, user}
    end

    In the above example, the resolver function create_user takes the name and email from the input argument and inserts a new user into the database using Ecto.

These examples provide a glimpse into the power and flexibility of Absinthe for building GraphQL APIs in Elixir. For more information and advanced usage, refer to the official Absinthe documentation: Absinthe Documentation

In conclusion, Absinthe is a feature-rich and highly customizable GraphQL toolkit for Elixir. Its intuitive syntax, powerful features, and seamless integration with Elixir make it an excellent choice for building scalable and efficient GraphQL APIs.