88
99Julia client for [ SurrealDB] ( https://surrealdb.com ) . Talks to a remote
1010` surreal ` server over WebSocket or HTTP, or runs the database in-process
11- via ` libsurreal ` . Same API for both.
11+ via ` libsurreal ` . Same API regardless of backend. Runs against SurrealDB
12+ v2 and v3.
1213
13- Cross-tested against the official [ Go] ( https://github.com/surrealdb/surrealdb.go )
14- and [ Python] ( https://github.com/surrealdb/surrealdb.py ) SDKs: 12 testsets
15- ported from ` surrealdb.go/db_test.go ` , plus an interop harness round-tripping
16- fixtures (Python ↔ Julia, Julia → Go). Runs against SurrealDB v2 and v3.
14+ ** Status: alpha.** Pre-1.0. API may break between minor versions
15+ (` 0.x ` → ` 0.(x+1) ` ). Pin a specific version to avoid breakage between
16+ bumps.
17+
18+ ## Install
19+
20+ Not yet in the General registry. Install via the repo URL, pinned to a
21+ tagged release:
22+
23+ ``` julia
24+ using Pkg
25+ Pkg. add (url= " https://github.com/danvinci/surrealdb" , rev= " v0.2.0-alpha.1" )
26+ ```
27+
28+ For the embedded backend you also need ` libsurreal ` . See
29+ [ Embedded mode] ( #embedded-mode ) below.
1730
1831## Quickstart
1932
@@ -53,13 +66,30 @@ The client is closed in a `finally`.
5366| ` mem:// ` | Embedded | In-memory, in-process via ` libsurreal ` |
5467| ` surrealkv://path ` | Embedded | File-backed, in-process |
5568
56- Embedded mode needs the ` libsurreal ` shared library:
69+ The ` mem:// ` and ` surrealkv://path ` schemes are SDK conventions (not part
70+ of SurrealDB's wire protocol). They tell ` connect() ` to load ` libsurreal `
71+ instead of opening a socket.
72+
73+ ### Embedded mode
74+
75+ Requires the ` libsurreal ` shared library. Build it once from
76+ [ ` surrealdb/surrealdb.c ` ] ( https://github.com/surrealdb/surrealdb.c ) :
77+
78+ ``` bash
79+ julia --project=. deps/build_libsurreal.jl
80+ ```
81+
82+ Needs a Rust toolchain (` cargo ` ) and ~ 15 min of build time. The resulting
83+ ` libsurrealdb_c.{so,dylib,dll} ` lands at the repo root.
5784
5885``` julia
59- SurrealDB. libsurreal_load! (" /path/to/libsurreal .dylib" ) # or set $SURREALDB_LIB
86+ SurrealDB. libsurreal_load! (" /path/to/libsurrealdb_c .dylib" ) # or set $SURREALDB_LIB
6087db = SurrealDB. connect (" mem://" )
6188```
6289
90+ A JLL package (` libsurrealdb_c_jll ` ) for one-line install via the registry
91+ is planned. See [ Roadmap] ( #roadmap ) .
92+
6393## Auth
6494
6595``` julia
@@ -100,7 +130,8 @@ alice = SurrealDB.create(db, User, "user",
100130
101131## Tables.jl
102132
103- Query results conform to ` Tables.jl ` (rows and columns):
133+ Query results conform to ` Tables.jl ` , so they plug into DataFrames, CSV.jl,
134+ Arrow.jl, or anything that consumes the Tables interface:
104135
105136``` julia
106137using DataFrames
@@ -109,25 +140,28 @@ df = DataFrame(result)
109140```
110141
111142` query_one(db, sql) ` asserts a single statement and returns one table.
112- ` query_table(db, sql) ` keeps multi-statement boundaries on remote (the
113- embedded backend collapses them).
143+ ` query_table(db, sql) ` returns one ` QueryResultTable ` per ` ; ` -separated
144+ statement on remote. The embedded backend flattens them into a single
145+ result.
114146
115147## Live queries
116148
117149``` julia
118150sub = SurrealDB. live (db, " user" )
119- @async for n:: SurrealDB.LiveNotification in sub
151+ task = @async for n:: SurrealDB.LiveNotification in sub
120152 @info " live event" action= n. action record= n. record data= n. result
121153end
122154
123- SurrealDB. kill! (sub)
155+ # Stop the subscription:
156+ SurrealDB. kill! (sub) # closes the channel; the @async for-loop exits
157+ wait (task)
124158```
125159
126160Each notification is a ` LiveNotification ` with typed fields
127- (` action ` , ` query_id ` , ` record ` , ` result ` , ` session ` ); it also subtypes
128- ` AbstractDict ` so legacy ` n["action"] ` access keeps working . After a
129- reconnect the SDK re-issues ` LIVE SELECT ` and overwrites ` sub.query_id `
130- with the new server-assigned UUID, so caller-held handles keep working.
161+ (` action ` , ` query_id ` , ` record ` , ` result ` , ` session ` ). It also subtypes
162+ ` AbstractDict ` , so ` n["action"] ` still works . After a reconnect the SDK
163+ re-issues ` LIVE SELECT ` and overwrites ` sub.query_id ` with the new
164+ server-assigned UUID, so caller-held handles keep working.
131165
132166## Running functions
133167
@@ -141,6 +175,8 @@ functions like `type::is::array` are SQL-only and must go through
141175
142176## Sessions and transactions
143177
178+ For v2 servers, the RPC-level helpers work:
179+
144180``` julia
145181SurrealDB. begin! (db)
146182try
@@ -150,27 +186,41 @@ catch
150186 SurrealDB. cancel! (db)
151187 rethrow ()
152188end
189+ ```
190+
191+ For ** v3+ remote servers** , the ` begin! ` /` commit! ` /` cancel! ` RPC methods
192+ expect a session-scoped transaction UUID; prefer raw SurrealQL:
153193
194+ ``` julia
195+ SurrealDB. query (db, """
196+ BEGIN TRANSACTION;
197+ CREATE user CONTENT { name: 'Bob' };
198+ COMMIT TRANSACTION;
199+ """ )
200+ ```
201+
202+ Session variables (` let! ` /` unset! ` ) work the same way on both:
203+
204+ ``` julia
154205SurrealDB. let! (db, " min_age" , 18 )
155206SurrealDB. query (db, " SELECT * FROM user WHERE age >= \$ min_age" )
156207SurrealDB. unset! (db, " min_age" )
157208```
158209
159210## Graph traversal (MetaGraphsNext)
160211
161- ` to_metagraph ` loads via a Pkg extension when ` MetaGraphsNext ` and
162- ` Graphs ` are present:
212+ Available via a Pkg extension. Loads automatically when ` MetaGraphsNext `
213+ and ` Graphs ` are present in your environment alongside ` SurrealDB ` :
163214
164215``` julia
165- using MetaGraphsNext, Graphs
216+ using SurrealDB, MetaGraphsNext, Graphs
166217g = SurrealDB. to_metagraph (db,
167218 " SELECT id, name FROM user" ,
168219 " SELECT id, in, out FROM follows" )
169220```
170221
171222Vertex labels are ` RecordID ` strings; vertex and edge data are field
172- dicts. The SDK does not auto-coerce results into a graph; call
173- ` to_metagraph ` when you want one.
223+ dicts. The SDK does not auto-coerce results into a graph.
174224
175225## Errors
176226
@@ -195,6 +245,9 @@ SurrealError
195245└── EmbeddedFFIError .op, .message
196246```
197247
248+ Catch a specific subtype for branch logic, or catch ` ServerError ` to
249+ handle any server-side failure uniformly:
250+
198251``` julia
199252try
200253 SurrealDB. create (db, " user:alice" , Dict (... ))
@@ -205,7 +258,7 @@ catch e::SurrealDB.ServerError
205258end
206259```
207260
208- The wire-format ` kind ` field maps to the Julia subtype. Legacy servers
261+ The wire-format ` kind ` field maps to the Julia subtype. Older servers
209262that emit only a JSON-RPC ` code ` go through a code-to-kind table.
210263
211264## Reconnect
@@ -214,28 +267,29 @@ WebSocket connections auto-reconnect on drop. Tune via `connect` kwargs:
214267
215268``` julia
216269db = SurrealDB. connect (" ws://localhost:8000" ;
217- reconnect = true , # set false to disable retries
270+ reconnect = true , # false disables retries
218271 reconnect_max_attempts = 10 ,
219- reconnect_base_delay = 0.5 , # exponential
272+ reconnect_base_delay = 0.5 , # seconds; exponential backoff
220273 reconnect_max_delay = 30.0 ,
221- reconnect_jitter = 0.1 , # [0, 1]
222- ping_interval = 30.0 , # 0 disables keepalive
274+ reconnect_jitter = 0.1 , # fraction of delay added randomly (0..1)
275+ ping_interval = 30.0 , # seconds; 0 disables keepalive
276+ rpc_timeout = 30.0 , # seconds; Inf disables per-RPC timeout
223277)
224278```
225279
226280Subscribe to lifecycle:
227281
228282``` julia
229283ch = SurrealDB. events (db)
230- @async for ev in ch
231- @info " lifecycle" event= ev # STATUS_CONNECTING, STATUS_CONNECTED, STATUS_RECONNECTING, STATUS_DISCONNECTED
284+ @async for ev:: SurrealDB.ConnectionStatus in ch
285+ @info " lifecycle" event= ev
286+ # ev ∈ (STATUS_CONNECTING, STATUS_CONNECTED, STATUS_RECONNECTING, STATUS_DISCONNECTED)
232287end
233288```
234289
235- Events are values of the ` ConnectionStatus ` enum (exported alongside
236- the four ` STATUS_* ` constants). ` STATUS_CONNECTED ` fires after state
237- replay (` use! ` , ` authenticate! ` , live re-subscription) finishes, not
238- before.
290+ ` STATUS_CONNECTED ` fires after state replay (` use! ` , ` authenticate! ` , live
291+ re-subscription) finishes, not before. Observers never see a half-restored
292+ session.
239293
240294## Debugging
241295
@@ -252,6 +306,7 @@ RPC traces emit on Julia's `@debug` channel. Enable with
252306- Julia 1.9 or newer
253307- Remote: SurrealDB server v2.x or v3.x
254308- Embedded: ` libsurreal ` from [ ` surrealdb/surrealdb.c ` ] ( https://github.com/surrealdb/surrealdb.c )
309+ (see [ Embedded mode] ( #embedded-mode ) for build instructions)
255310
256311## Testing
257312
@@ -263,9 +318,26 @@ The suite has three layers:
263318
264319- ** Unit** (no network): types, error parser, FFI marshalling,
265320 reconnect state machine, integration tests against an in-process
266- mock WebSocket server.
321+ mock WebSocket server, MetaGraphsNext extension .
267322- ** Integration** (needs ` surreal start --bind 127.0.0.1:8001 ` ):
268323 connection lifecycle, auth, query, methods, sessions, live queries.
269324- ** Embedded** (needs ` libsurreal ` ): full FFI roundtrip.
270325
271326Layers self-skip when their prerequisite is missing.
327+
328+ Also cross-tested against the official
329+ [ Go] ( https://github.com/surrealdb/surrealdb.go ) and
330+ [ Python] ( https://github.com/surrealdb/surrealdb.py ) SDKs: 12 testsets
331+ ported from ` surrealdb.go/db_test.go ` , plus an interop harness
332+ round-tripping fixtures (Python ↔ Julia, Julia → Go).
333+
334+ ## Roadmap
335+
336+ - ` libsurrealdb_c_jll ` : ship pre-built dylibs via Yggdrasil for
337+ one-line ` Pkg.add ` install and CI speedup.
338+ - CBOR transport: smaller payloads, native Duration/Decimal round-trip.
339+ - General registry submission once API stabilizes.
340+
341+ ## License
342+
343+ MIT. See [ LICENSE] ( LICENSE ) .
0 commit comments