Elixir client for NebulaGraph — a distributed open-source graph database.
nebula_graph_ex provides:
- Connection pooling via
DBConnection(the same library used by Postgrex and MyXQL). - Full type decoding — NebulaGraph's
Valueunion is converted to idiomatic Elixir terms: integers, floats, binaries, booleans,nil,%Vertex{},%Edge{},%Path{}, date/time structs, geography, and more. - Parameterised queries — values are encoded as Thrift
Valuestructs, never interpolated into statement strings. - TLS / SSL support out of the box.
- Telemetry events for every query start, stop, and exception.
Quick start
1. Add to mix.exs
defp deps do
[
{:nebula_graph_ex, "~> 0.1"}
]
end2. Define a graph module
Borrowing from Ecto's Repo pattern, create a module for your graph connection:
# lib/my_app/graph.ex
defmodule MyApp.Graph do
use NebulaGraphEx.Graph, otp_app: :my_app
end3. Configure it
# config/config.exs
config :my_app, MyApp.Graph,
hostname: "localhost",
port: 9669,
space: "my_graph",
pool_size: 10
# config/runtime.exs — secrets resolved at boot, not compile time
config :my_app, MyApp.Graph,
hostname: System.fetch_env!("NEBULA_HOST"),
password: fn -> System.fetch_env!("NEBULA_PASS") end4. Add to your supervision tree
def start(_type, _args) do
children = [MyApp.Graph]
Supervisor.start_link(children, strategy: :one_for_one)
end5. Run queries
alias NebulaGraphEx.{Record, ResultSet}
# Simple query
{:ok, rs} = MyApp.Graph.query("MATCH (v:Player) RETURN v.name, v.age LIMIT 5")
rs |> ResultSet.count()
#=> 5
rs |> ResultSet.rows() |> Enum.map(&Record.to_map/1)
#=> [%{"v.name" => "Tim Duncan", "v.age" => 42}, ...]
# Parameterised query (always use params for user input)
{:ok, rs} =
MyApp.Graph.query(
"MATCH (v:Player{name: $name}) RETURN v.age AS age",
%{"name" => "Tim Duncan"}
)
rs |> ResultSet.first!() |> Record.get!("age")
#=> 42
# Bang variant raises NebulaGraphEx.Error on failure
MyApp.Graph.query!("RETURN 1+1 AS n")
|> ResultSet.first!()
|> Record.get!("n")
#=> 26. Check connection status
{:ok, status} = MyApp.Graph.status()
status.connected?
#=> true
status.queryable?
#=> true
{:error, failed_status} = MyApp.Graph.status(probe_statement: "THIS IS NOT VALID NGQL")
failed_status.error_details
#=> %{code: :e_syntax_error, message: "...", category: :query, statement: "THIS IS NOT VALID NGQL"}Working with graph types
When a column contains a vertex, edge, or path, it is decoded automatically:
{:ok, rs} = MyApp.Graph.query("MATCH (v:Player) RETURN v LIMIT 1")
rs
|> ResultSet.first!()
|> Record.get!("v")
#=> %NebulaGraphEx.Types.Vertex{
# vid: "player100",
# tags: [%NebulaGraphEx.Types.Tag{name: "Player", props: %{"name" => "Tim Duncan"}}]
# }
rs
|> ResultSet.first!()
|> Record.get!("v")
|> NebulaGraphEx.Types.Vertex.prop("Player", "name")
#=> "Tim Duncan"Per-query options
Options can be overridden at call-site:
MyApp.Graph.query(stmt, %{},
space: "other_space",
timeout: 60_000,
decode_mapper: &Record.to_map/1
)Configuration reference
All options accepted by NebulaGraphEx.Graph.start_link/1 are documented in
NebulaGraphEx.Options.
Telemetry
Attach the default logger handler in development:
NebulaGraphEx.Telemetry.attach_default_handler()Or attach your own handler for production metrics — see NebulaGraphEx.Telemetry
for event names, measurements, and metadata shapes.
Updating the Thrift IDL
NebulaGraph's wire protocol is defined by the Thrift IDL files in
priv/thrift/. To upgrade to a newer NebulaGraph server version:
- Replace the files in
priv/thrift/with the upstream versions from vesoft-inc/nebula. - Re-add the
namespace elixirlines (the only lines that differ from upstream). - Run
mix compile.