A driver for Neo4j that speaks the Bolt protocol, with two implementations behind one public Ruby API:
Bundler installs the implementation matching your platform automatically, and
your code is identical either way. The gem version tracks the Java-driver
version it targets (e.g. 6.2.x).
UnsupportedTypeexecute_query authbolt+s/bolt+ssc schemes, and mutual-TLS client
certificates# Gemfile
gem 'neo4j-ruby-driver'
bundle install
require 'neo4j/driver'
Neo4j::Driver::GraphDatabase.driver(
'bolt://localhost:7687',
Neo4j::Driver::AuthTokens.basic('neo4j', 'password')
) do |driver|
driver.session do |session|
result = session.run('RETURN 1 AS num')
puts result.single[:num] # => 1
end
end
Use a bolt:// URL for a single instance, neo4j:// for a routed (cluster)
connection, and the +s/+ssc variants for TLS.
driver.session do |session|
node = session.run(
'CREATE (n:Person {name: $name, age: $age}) RETURN n',
name: 'Alice', age: 30
).single[:n]
puts node[:name] # => "Alice"
end
session.run(query, parameters = {}, config = {}) keeps parameters and config
as separate explicit hashes.
Explicit transactions are default-rollback — you must call tx.commit.
driver.session do |session|
session.begin_transaction do |tx|
tx.run('CREATE (:Person {name: "Bob"})')
tx.run('CREATE (:Person {name: "Charlie"})')
tx.commit
end
end
Auto-commit on clean exit; transient failures are retried with exponential backoff.
driver.session do |session|
session.execute_write do |tx|
tx.run('CREATE (n:Person {name: "Diana"}) RETURN n').single
end
people = session.execute_read do |tx|
tx.run('MATCH (p:Person) RETURN p.name AS name').collect { |r| r[:name] }
end
end
result = session.run('MATCH (p:Person) RETURN p.name AS name, p.age AS age')
result.each { |record| puts "#{record[:name]} is #{record[:age]}" }
record = result.single # exactly one row
name = record[:name] # by key (string or symbol)
age = record[1] # or by index
records = result.to_a
Node labels and relationship types come back as symbols off the entity —
node.labels # => [:Person], rel.type # => :KNOWS — as do record columns and
map/property keys. Cypher functions that return strings (labels(), keys(),
type()) return strings, because their result is an ordinary value on the
wire, indistinguishable from data you returned yourself. Read the accessor for
symbols, or convert the function result with .map(&:to_sym).
The development tree is split so shared code lives in one place and each implementation adds only its own wire layer:
lib/
├── shared/ # public API + shared types, loaded by both implementations
├── mri/ # pure-Ruby Bolt protocol, PackStream, connection pool
└── jruby/ # thin wrapper over the official Java driver jars
The published gem is flattened to a single lib/ for the platform via a staged
build (see JRUBY.md). See CLAUDE.md for the layout and conventions,
DEVELOPMENT.md for the dev loop, and DECISIONS.md for architectural history.
tzinfo, zeitwerk, connection_pool — no Java, no server-side
components.neo4j-java-driver jars, resolved by
jar-dependencies; runs on a JVM (Java 17+).export TEST_NEO4J_URL=bolt://localhost:7687
export TEST_NEO4J_USER=neo4j
export TEST_NEO4J_PASS=password
bundle exec rspec
spec/shared/integration/ — end-to-end against a running Neo4j instancespec/shared/neo4j/driver/ — public-API unit testsspec/mri/, spec/jruby/ — implementation-specific testsConformance is additionally exercised through the Neo4j
testkit suite via the Ruby backend
under testkit-backend/.
Contributions are welcome. Keep the public API flavour-agnostic (no
implementation type may leak across it), follow the conventions in CLAUDE.md,
and add coverage on both implementations. See CHANGELOG.md for recent changes.
Released under the MIT License.
Ruby
97.8%
Shell
1.4%
A driver for Neo4j that speaks the Bolt protocol, with two implementations behind one public Ruby API:
Bundler installs the implementation matching your platform automatically, and
your code is identical either way. The gem version tracks the Java-driver
version it targets (e.g. 6.2.x).
UnsupportedTypeexecute_query authbolt+s/bolt+ssc schemes, and mutual-TLS client
certificates# Gemfile
gem 'neo4j-ruby-driver'
bundle install
require 'neo4j/driver'
Neo4j::Driver::GraphDatabase.driver(
'bolt://localhost:7687',
Neo4j::Driver::AuthTokens.basic('neo4j', 'password')
) do |driver|
driver.session do |session|
result = session.run('RETURN 1 AS num')
puts result.single[:num] # => 1
end
end
Use a bolt:// URL for a single instance, neo4j:// for a routed (cluster)
connection, and the +s/+ssc variants for TLS.
driver.session do |session|
node = session.run(
'CREATE (n:Person {name: $name, age: $age}) RETURN n',
name: 'Alice', age: 30
).single[:n]
puts node[:name] # => "Alice"
end
session.run(query, parameters = {}, config = {}) keeps parameters and config
as separate explicit hashes.
Explicit transactions are default-rollback — you must call tx.commit.
driver.session do |session|
session.begin_transaction do |tx|
tx.run('CREATE (:Person {name: "Bob"})')
tx.run('CREATE (:Person {name: "Charlie"})')
tx.commit
end
end
Auto-commit on clean exit; transient failures are retried with exponential backoff.
driver.session do |session|
session.execute_write do |tx|
tx.run('CREATE (n:Person {name: "Diana"}) RETURN n').single
end
people = session.execute_read do |tx|
tx.run('MATCH (p:Person) RETURN p.name AS name').collect { |r| r[:name] }
end
end
result = session.run('MATCH (p:Person) RETURN p.name AS name, p.age AS age')
result.each { |record| puts "#{record[:name]} is #{record[:age]}" }
record = result.single # exactly one row
name = record[:name] # by key (string or symbol)
age = record[1] # or by index
records = result.to_a
Node labels and relationship types come back as symbols off the entity —
node.labels # => [:Person], rel.type # => :KNOWS — as do record columns and
map/property keys. Cypher functions that return strings (labels(), keys(),
type()) return strings, because their result is an ordinary value on the
wire, indistinguishable from data you returned yourself. Read the accessor for
symbols, or convert the function result with .map(&:to_sym).
The development tree is split so shared code lives in one place and each implementation adds only its own wire layer:
lib/
├── shared/ # public API + shared types, loaded by both implementations
├── mri/ # pure-Ruby Bolt protocol, PackStream, connection pool
└── jruby/ # thin wrapper over the official Java driver jars
The published gem is flattened to a single lib/ for the platform via a staged
build (see JRUBY.md). See CLAUDE.md for the layout and conventions,
DEVELOPMENT.md for the dev loop, and DECISIONS.md for architectural history.
tzinfo, zeitwerk, connection_pool — no Java, no server-side
components.neo4j-java-driver jars, resolved by
jar-dependencies; runs on a JVM (Java 17+).export TEST_NEO4J_URL=bolt://localhost:7687
export TEST_NEO4J_USER=neo4j
export TEST_NEO4J_PASS=password
bundle exec rspec
spec/shared/integration/ — end-to-end against a running Neo4j instancespec/shared/neo4j/driver/ — public-API unit testsspec/mri/, spec/jruby/ — implementation-specific testsConformance is additionally exercised through the Neo4j
testkit suite via the Ruby backend
under testkit-backend/.
Contributions are welcome. Keep the public API flavour-agnostic (no
implementation type may leak across it), follow the conventions in CLAUDE.md,
and add coverage on both implementations. See CHANGELOG.md for recent changes.
Released under the MIT License.
Ruby
97.8%
Shell
1.4%