tutorial

How to Monitor Absinthe with Vigilmon

Absinthe is Elixir's GraphQL toolkit — but GraphQL's single-endpoint design makes traditional uptime monitoring miss slow resolvers, failed subscriptions, and schema errors. Here's how to monitor Absinthe APIs end-to-end with Vigilmon.

How to Monitor Absinthe with Vigilmon

Absinthe brings a full-featured GraphQL server to Elixir and Phoenix — subscriptions over WebSockets, batched field resolution via Dataloader, and a composable plug pipeline. But GraphQL has a monitoring blind spot: every request hits the same /graphql endpoint and returns HTTP 200, even when resolvers error out. Standard uptime monitors see a healthy endpoint when your query is actually returning {"errors": [...]}.

This tutorial wires up layered monitoring for an Absinthe API:

  • A dedicated GraphQL health query that exercises the schema end-to-end
  • HTTP uptime monitoring with Vigilmon
  • Resolver error-rate alerting via cron heartbeats
  • Subscription channel health checks
  • Slack alerts when queries degrade

Why Monitor Absinthe?

| Signal | What it catches | |---|---| | Schema availability | Deploy regressions that break introspection | | Resolver latency | N+1 queries, Dataloader bottlenecks | | Subscription channels | WebSocket transport failures, PubSub outages | | Error rate | Authorization bugs, missing fields, bad variables | | Batch performance | Dataloader batching configuration issues |

A plain TCP check misses all of these. You need to actually execute queries.


Step 1: Add a Health Query to Your Schema

Create a dedicated health query that Vigilmon can execute. This exercises your schema, the Absinthe plug pipeline, and any configured middleware.

# lib/my_app_web/schema.ex
defmodule MyAppWeb.Schema do
  use Absinthe.Schema

  query do
    field :health, :health_result do
      resolve fn _args, _info ->
        {:ok, %{status: "ok", timestamp: DateTime.utc_now() |> DateTime.to_iso8601()}}
      end
    end

    # ... your other queries
  end

  object :health_result do
    field :status, non_null(:string)
    field :timestamp, non_null(:string)
  end
end

Test it locally:

curl -X POST http://localhost:4000/graphql \
  -H "Content-Type: application/json" \
  -d '{"query": "{ health { status timestamp } }"}'
# => {"data":{"health":{"status":"ok","timestamp":"2026-07-03T10:00:00Z"}}}

Step 2: Add an HTTP Health Endpoint

Vigilmon monitors HTTP endpoints, not GraphQL queries directly. Add a lightweight REST health endpoint alongside your GraphQL route so Vigilmon has a clean target:

# lib/my_app_web/plugs/graphql_health.ex
defmodule MyAppWeb.Plugs.GraphQLHealth do
  import Plug.Conn

  def init(opts), do: opts

  def call(%Plug.Conn{request_path: "/health/graphql"} = conn, _opts) do
    result = run_health_query()
    {status, body} = case result do
      {:ok, %{"data" => %{"health" => %{"status" => "ok"}}}} ->
        {200, %{status: "ok", graphql: "reachable"}}
      _ ->
        {503, %{status: "error", graphql: "unreachable"}}
    end

    conn
    |> put_resp_content_type("application/json")
    |> send_resp(status, Jason.encode!(body))
    |> halt()
  end

  def call(conn, _opts), do: conn

  defp run_health_query do
    query = ~s|{ health { status timestamp } }|
    Absinthe.run(query, MyAppWeb.Schema)
  end
end

Register the plug in your endpoint before the router:

# lib/my_app_web/endpoint.ex
plug MyAppWeb.Plugs.GraphQLHealth
plug MyAppWeb.Router

Now GET /health/graphql returns 200 when Absinthe is healthy and 503 when it's not.


Step 3: Add Vigilmon Uptime Monitoring

  1. Log in to vigilmon.online and click Add Monitor.
  2. Set Type to HTTP / HTTPS.
  3. Enter your health endpoint URL: https://api.yourapp.com/health/graphql.
  4. Set Check interval to 1 minute.
  5. Set Expected HTTP status to 200.
  6. Under Advanced, add a Response body contains check: "status":"ok".
  7. Click Save.

Vigilmon now hits your GraphQL health endpoint every minute and alerts you if the schema becomes unreachable.


Step 4: Monitor Resolver Error Rates with a Heartbeat

GraphQL resolvers can fail silently — HTTP 200 with {"errors": [...]} in the body. Use Absinthe's telemetry events to track error rates and emit a heartbeat to Vigilmon.

First, attach a telemetry handler in your application:

# lib/my_app/graphql_telemetry.ex
defmodule MyApp.GraphQLTelemetry do
  def setup do
    :telemetry.attach(
      "absinthe-execute",
      [:absinthe, :execute, :operation, :stop],
      &handle_event/4,
      nil
    )
  end

  def handle_event(_event, _measurements, metadata, _config) do
    case metadata do
      %{blueprint: %{result: %{errors: errors}}} when errors != [] ->
        :telemetry.execute([:my_app, :graphql, :errors], %{count: length(errors)}, %{})
      _ ->
        :ok
    end
  end
end

Attach it in application.ex:

def start(_type, _args) do
  MyApp.GraphQLTelemetry.setup()
  # ...
end

Then add a periodic heartbeat worker that pings Vigilmon only when the error rate is acceptable:

# lib/my_app/graphql_heartbeat.ex
defmodule MyApp.GraphQLHeartbeat do
  use GenServer

  @interval :timer.minutes(1)
  @vigilmon_heartbeat_url "https://vigilmon.online/api/heartbeat/YOUR_HEARTBEAT_ID"

  def start_link(_), do: GenServer.start_link(__MODULE__, nil, name: __MODULE__)

  def init(_) do
    schedule()
    {:ok, %{error_count: 0}}
  end

  def handle_info(:ping, state) do
    if state.error_count < 10 do
      HTTPoison.get!(@vigilmon_heartbeat_url)
    end
    schedule()
    {:noreply, %{state | error_count: 0}}
  end

  defp schedule, do: Process.send_after(self(), :ping, @interval)
end

Add it to your supervision tree. If GraphQL errors spike, the heartbeat stops and Vigilmon alerts you.

To create the heartbeat monitor in Vigilmon:

  1. Click Add MonitorCron / Heartbeat.
  2. Set the expected ping interval to 1 minute.
  3. Copy the generated heartbeat URL into @vigilmon_heartbeat_url above.

Step 5: Monitor Absinthe Subscriptions

Subscriptions run over Phoenix Channels. Monitor the channel endpoint as well:

  1. Click Add MonitorHTTP / HTTPS.
  2. Enter your WebSocket upgrade URL as an HTTP check: https://api.yourapp.com/socket/websocket.
  3. Set Expected HTTP status to 200 (Phoenix returns 200 on HTTP upgrade requests).
  4. Set the interval to 2 minutes.

For deeper subscription health, add a subscription-specific health query:

# In your schema
subscription do
  field :health_ping, :string do
    config fn _args, _info ->
      {:ok, topic: "health"}
    end
    resolve fn ping, _, _ -> {:ok, ping} end
  end
end

Then periodically publish a test ping from a GenServer:

defmodule MyApp.SubscriptionHealthWorker do
  use GenServer

  def handle_info(:ping, state) do
    Absinthe.Subscription.publish(MyAppWeb.Endpoint, "pong", health_ping: "health")
    Process.send_after(self(), :ping, 30_000)
    {:noreply, state}
  end
end

Step 6: Set Up Alerts

In Vigilmon, configure alert channels for your Absinthe monitors:

  1. Go to Alert ChannelsAdd Channel.
  2. Choose Slack, Email, or PagerDuty.
  3. Set alert thresholds: notify after 2 consecutive failures (avoids flapping on transient errors).
  4. Assign the alert channel to both your HTTP monitor and your heartbeat monitor.

For Absinthe APIs used by mobile clients, also enable Status Page so users can check GraphQL availability themselves:

  1. Go to Status PagesCreate Page.
  2. Add your GraphQL health monitor.
  3. Share the public URL with API consumers.

Key Metrics to Watch

| Metric | Vigilmon feature | What to alert on | |---|---|---| | Schema availability | HTTP monitor on /health/graphql | Any 5xx or timeout | | Resolver error rate | Heartbeat monitor | Heartbeat missing for > 2 min | | Response time | Response time chart | P95 > 2s over 5 minutes | | SSL certificate | Cert expiry monitor | Expires in < 14 days | | Subscription endpoint | HTTP monitor on /socket/websocket | Any non-200 response |


Conclusion

Absinthe's flexibility makes it a great GraphQL server — but that same flexibility means you need layered monitoring: an HTTP health endpoint for basic availability, a heartbeat that pauses on error spikes, and subscription endpoint checks. With these three Vigilmon monitors in place, you'll catch schema regressions, resolver failures, and transport outages before your API consumers do.

Get started free at vigilmon.online.

Monitor your app with Vigilmon

Free plan — 5 monitors, no credit card required. Up and running in 60 seconds.

Start free →