package neodriver
Install
dune-project
Dependency
Authors
Maintainers
Sources
md5=ddea08803bc57d4928a9f13de54461ae
sha512=d5dc0b69af7972944332b243a28ccc2b15adb62ee9129023cf5a3ea57faf13b048e79579fe03b73d0a17c8c84e32ab92f27de6e2579c59ce4bb5bf8b5131100c
doc/usage.html
Usage
This guide walks through the driver's main API. If you have not connected yet, start with the quickstart. Runnable programs live in the examples directory.
Packages and what to open
open Neodriver exposes the whole driver: Driver, Conn, Session, Tx, Neo4jResult, Summary, and the core types Errors, Config, Addressing, Values, Temporal, Hydration and Packstream. Programs also need the eio_main library for the Eio_main.run entry point.
Connecting
let session =
Driver.connect ~uri:"bolt://localhost:7687"
~auth:(Conn.basic_auth ~credentials:"password" ())
net clock swDriver.connectparses the URI and returns a lazily connectingSession.t: the server is contacted only on the first query.Conn.basic_authbuilds the authentication token (default principalneo4j; only thebasicscheme is supported).?user_agent,?connection_timeout(seconds, default 30.0) and?config(aSession.config) tune the connection.- The
swswitch must outlive the session (it hosts the connection attempt). ?resolverreplaces address lookup (useful for custom DNS resolution).
Schemes and TLS
bolt://— none (plain).bolt+s://— TLS, certificate validated against the OS trust store.bolt+ssc://— TLS, any certificate accepted (self-signed allowed).
neo4j:// (routing) is not implemented: a neo4j:// URI fails on first use with a Service_unavailable error.
Sessions
Session.run sends an auto-commit query and returns a lazily streamed Neo4jResult.t:
match Session.run session ~query:"RETURN 1 AS n" ~parameters:[] with
| Ok result -> (
match Neo4jResult.values result with
| Ok [ [ Values.Int n ] ] -> Printf.printf "n = %Ld\n" n
| _ -> ())
| Error error -> failwith (Errors.to_string error)- Parameters are a
(string * Values.t) list. Neo4jResult.valuesdrains the result intoValues.t list list(one list per record).Neo4jResult.consumedrains and returns theSummary;next,peekandfetchiterate without consuming everything.- The session's bookmarks are updated automatically once an auto-commit result is consumed; read them with
Session.last_bookmarksand seed them viaSession.config'sbookmarks. Session.config(baseSession.default_config) also selects thedatabaseand the transaction retry settings.
Transactions
Explicit
let conn =
match Session.conn session with Ok conn -> conn | Error error -> failwith (Errors.to_string error)
in
let hydration = Conn.hydration conn in
match Session.begin_transaction session with
| Ok tx -> (
match
Tx.run tx ~hydration ~query:"CREATE (:Person {name: $name})"
~parameters:[ ("name", Values.String "Alice") ]
with
| Ok result -> (
match Neo4jResult.consume result with
| Ok _ -> (
match Tx.commit tx with
| Ok _ -> ()
| Error error ->
ignore (Tx.rollback tx);
failwith (Errors.to_string error))
| Error error ->
ignore (Tx.rollback tx);
failwith (Errors.to_string error))
| Error error ->
ignore (Tx.rollback tx);
failwith (Errors.to_string error))
| Error error -> failwith (Errors.to_string error)Tx.runneeds a hydration scope, obtained withConn.hydration (Session.conn session).Tx.commitapplies the writes and returns the bookmark (string option);Tx.rollbackdiscards them. A session can hold only one open transaction.
Managed (with retry)
let conn =
match Session.conn session with Ok conn -> conn | Error error -> failwith (Errors.to_string error)
in
let hydration = Conn.hydration conn in
let created = ref 0 in
let work tx =
match
Tx.run tx ~hydration ~query:"CREATE (:Person {name: $name})"
~parameters:[ ("name", Values.String "Bob") ]
with
| Ok result -> (
match Neo4jResult.consume result with
| Ok summary ->
created := summary.counters.nodes_created;
Ok ()
| Error error -> Error (Session.Driver error))
| Error error -> Error (Session.Driver error)
in
match Session.execute session ~mode:Config.Write work with
| Ok () -> Printf.printf "created %d node(s)\n" !created
| Error (Session.Driver error) -> failwith (Errors.to_string error)
| Error Session.Client -> failwith "the application aborted the transaction"Session.executerunsworkinside a transaction and commits it onOk. Failures thatErrors.is_retryabletreats as retryable are retried (with a jittered backoff) untilmax_transaction_retry_timeruns out;Error (Session.Driver e)retries when retryable,Error Session.Clientnever does.- The work callback returns
(unit, Session.failure) result, so return query data through a ref, as above.
Authentication
Only basic. For Bolt >= 5.1 the token is sent via a separate LOGON message after HELLO; older versions inline it in HELLO. Conn.re_auth conn auth re-authenticates when the token changes, and Conn.logon/Conn.logoff manage the authenticated state directly.
Value types
Values.t is a plain variant, so parameters are built explicitly — there is no implicit conversion from OCaml data. The common cases:
true/false→Values.Bool b42L→Values.Int n(anint64)3.14→Values.Float f"hello"→Values.String s- a list of values →
Values.List [ Values.Int 1L; Values.Int 2L ] - a record →
Values.Map [ ("key", value); ... ] None(an absent value) →Values.Null
Integers are int64 — use 42L, not 42. Null is also how you represent None inside a List or Map.
Graph, spatial, temporal and vector values are built through their types:
let born = Temporal.DateTime.of_ymd_hms (1990, 5, 17) (12, 0, 0) 0 |> Option.get in
let home = Values.Point { srid = 4326; x = 21.0122; y = 52.2297; z = None } in
let params =
[
("name", Values.String "Alice");
("born", Values.DateTime born);
("home", home);
("tags", Values.List [ Values.String "admin"; Values.String "staff" ]);
("meta", Values.Map [ ("active", Values.Bool true) ]);
]A small helper makes converting a custom record convenient:
let person_to_values { name; age; tags } =
Values.Map
[
("name", Values.String name);
("age", Values.Int age);
("tags", Values.List (List.map (fun t -> Values.String t) tags));
]Reading values back is pattern matching:
match value with | Values.Int n -> Printf.printf "int %Ld\n" n | Values.String s -> Printf.printf "string %s\n" s | Values.List items -> List.iter print_value items | Values.Map fields -> List.iter (fun (k, v) -> ...) fields | Values.Node node -> Printf.printf "%s\n" (String.concat "," node.labels) | Values.Broken b -> (* the driver could not decode it *) | _ -> ()
Values.to_string renders any value for logging. The graph types (Node, Relationship, Path) are typically read from results, not sent as parameters.
Temporal provides Date, Time, DateTime and Duration. Named time zones resolve through the embedded IANA database (1970-2040) with an LMT fallback before 1970; DateTime.of_ymd_hms, to_ymd_hms and offset_seconds handle the wall-clock/epoch conversions. Hydration converts between PackStream and Values.t; you rarely touch it directly.
Errors
Session.run, Tx.run and friends return (_, Errors.t) result. Errors.t covers server errors (Neo4j of { code; message; classification }), Service_unavailable, Transaction_error, Configuration_error and more. Errors.to_string renders a message; Errors.is_retryable tells you whether a failure is worth retrying.
Configuration
Driver.connect options
uri(required) — e.g.bolt://localhost:7687,bolt+s://host:7687,bolt+ssc://host:7687(neo4j://not supported yet).auth(required) — fromConn.basic_auth ?principal ?credentials ()(principal defaults toneo4j, credentials to the empty string).?user_agent— the HELLO user agent (defaultConn.default_user_agent).?connection_timeout(seconds) — bounds the whole connect attempt and subsequent reads/writes (default 30.0;infinitydisables the deadline).?config— aSession.config(see below).?resolver—Addressing.t -> (Addressing.t list, Errors.t) result; replaces the address lookup, each returned address being tried in turn.
Session settings (Session.config)
Base it on Session.default_config and update only what you need:
let config = { Session.default_config with database = Some "mydb"; bookmarks = [ "bm-1" ] } in
Driver.connect ~uri ~auth ~config net clock swdatabase— database selected in RUN/BEGIN (honored).access_mode—Read/Write, sent in BEGIN for explicit and managed transactions (honored for explicit/managed).bookmarks— initial bookmarks, sent in RUN/BEGIN (honored).impersonated_user— user to impersonate, sent in BEGIN (honored for explicit/managed).max_transaction_retry_time— retry budget ofSession.execute(default 30.0 s, honored).initial_retry_delay— first backoff ofSession.execute(default 1.0 s, honored).retry_delay_multiplier— backoff growth (default 2.0, honored).retry_delay_jitter_factor— backoff jitter (default 0.2, honored).fetch_size— stream batch size hint (accepted, not yet applied).
Query and transaction options
Session.run, Session.begin_transaction and Session.execute accept:
?timeout(seconds) — the transaction timeout, sent astx_timeout.?metadata— a(string * Values.t) listsent astx_metadata.Session.executeadditionally takes the requiredmode:Config.access_mode(or leave it out and set the session'saccess_mode).
Validated config records (Config)
Config.make_workspace_config and Config.make_pool_config build their records with validation (a Configuration_error on out-of-range values):
make_workspace_config:connection_acquisition_timeout,max_transaction_retry_time,initial_retry_delay,retry_delay_multiplier,retry_delay_jitter_factor,fetch_size,database,impersonated_user,disable_auto_commit_retries.make_pool_config:max_connection_lifetime,liveness_check_timeout,max_connection_pool_size,connection_timeout,connection_write_timeout,keep_alive,telemetry_disabled.
These mirror the Python driver's settings, but the connection pool is not implemented yet: each Driver.connect produces one session that owns one connection, so the pool options above and disable_auto_commit_retries have no effect for now.
Not yet implemented
- Routing:
neo4j://, server-side routing, home database. - Connection pool.
- Impersonation on auto-commit queries (it works in transactions via the BEGIN extra).
- Notification filtering and telemetry.
- The high-level API (
execute_query,verify_connectivity) and bookmark/auth managers.
See PLAN.md for the full roadmap.