Palm.SurrealDB.Net 0.2.1

dotnet add package Palm.SurrealDB.Net --version 0.2.1
                    
NuGet\Install-Package Palm.SurrealDB.Net -Version 0.2.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Palm.SurrealDB.Net" Version="0.2.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Palm.SurrealDB.Net" Version="0.2.1" />
                    
Directory.Packages.props
<PackageReference Include="Palm.SurrealDB.Net" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Palm.SurrealDB.Net --version 0.2.1
                    
#r "nuget: Palm.SurrealDB.Net, 0.2.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Palm.SurrealDB.Net@0.2.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Palm.SurrealDB.Net&version=0.2.1
                    
Install as a Cake Addin
#tool nuget:?package=Palm.SurrealDB.Net&version=0.2.1
                    
Install as a Cake Tool

Palm.SurrealDB.Net

The client package of the Palm.SurrealDB ecosystem — the primary, consumer-facing package most applications install. It ships SurrealDbClient, a single sealed implementation of ISurrealClient that binds SurrealDbOptions to a WebSocket or HTTP transport, a CBOR serializer, and a reflection-based type mapper, plus an AddSurrealDb extension for Microsoft.Extensions.DependencyInjection. Everything else in the ecosystem — the SurrealQL AST/emitter, the LINQ provider, the F# DSL — sits on top of, or alongside, what this package exposes. If you only take one dependency to talk to SurrealDB from .NET, this is it.

Targets net10.0 only. See the root README for the full ecosystem map, build/test commands, and project status.

Installation

dotnet add package Palm.SurrealDB.Net

This project is packable and ships as the Palm.SurrealDB.Net NuGet package. An earlier version of this section said the opposite, citing a true observation — the .csproj sets neither PackageId nor IsPackable — in support of a false conclusion: packability comes from the SDK default and Directory.Build.props, and the only projects that opt out are the three that set IsPackable=false. PackableSetGuardTests pins all 25 packable ids, and this is one of them.

Targets net10.0 only. The package is proprietary and sets PackageRequireLicenseAcceptance, so NuGet prompts before installing — see LICENSE.

Nothing is on NuGet.org until the first release runs (see docs/RELEASING.md); until then, reference the project directly:

<ItemGroup>
  <ProjectReference Include="path/to/src/Palm.SurrealDB.Net/Palm.SurrealDB.Net.csproj" />
</ItemGroup>

Contents

Quick start

using Palm.SurrealDB.Net;

await using var client = new SurrealDbClient(new SurrealDbOptions
{
    Endpoint = new Uri("ws://127.0.0.1:8000/rpc"),
    Namespace = "test",
    Database = "test",
    Credentials = new RootCredentials("root", "root"),
});

await client.ConnectAsync();

var ada = await client.CreateAsync(new Table("person"), new Person("Ada", 30));

SurrealDbClient

SurrealDbClient (Palm.SurrealDB.Net.SurrealDbClient) is sealed and implements the full ISurrealClient surface (defined in Palm.SurrealDB.Net.Abstractions; summarized here member by member since callers of this package need the full contract without hopping projects). Sessions initialize lazily on first use — connect, sign in, USE namespace/database — and are restored automatically after a WebSocket reconnect.

Constructor

public SurrealDbClient(
    SurrealDbOptions options,
    ISurrealConnection? connection = null,
    ISurrealTypeMapper? mapper = null,
    ISurrealSerializer? serializer = null)
Parameter Default Notes
options (required) Validated eagerly via options.Validate(); throws SurrealDbConfigurationException on invalid options (see SurrealDbOptions summary).
connection null When omitted, the transport is chosen from options.Endpoint.Scheme: ws/wss → WebSocketConnection, http/https → HttpConnection. The client owns and disposes a transport it creates; an externally supplied connection is not disposed by DisposeAsync. Pass a fake/mock ISurrealConnection for unit testing without a live server.
mapper new ReflectionSurrealTypeMapper() Implements ISurrealTypeMapper (from Palm.SurrealDB.Net.Serialization); override to plug in a source-generated/AOT-safe mapper. Exposed afterward via TypeMapper.
serializer CBOR (Palm.SurrealDB.Net.Protocol's default) Only used when the client creates its own transport (i.e. connection is null); ignored otherwise.

Connection lifecycle

Member Signature Behavior
ConnectAsync Task ConnectAsync(CancellationToken cancellationToken = default) Establishes the connection and runs the initial session sequence (authenticate/sign in, then USE). Idempotent — subsequent calls, and every other client method, share the same lazy one-time initialization guarded by an internal semaphore. Throws ObjectDisposedException if the client was disposed.
UseAsync Task UseAsync(string @namespace, string? database = null, CancellationToken cancellationToken = default) Selects namespace and, optionally, database. Updates the session's remembered namespace/database so a later reconnect restores it.
SignInAsync Task<string> SignInAsync(SurrealCredentials credentials, CancellationToken cancellationToken = default) Signs in with RootCredentials, NamespaceCredentials, DatabaseCredentials, AccessCredentials, or TokenCredentials and returns the session token (empty string if the server didn't return one). TokenCredentials short-circuits to AuthenticateAsync. Remembers the credentials for reconnect.
SignUpAsync Task<string> SignUpAsync(AccessCredentials credentials, CancellationToken cancellationToken = default) Signs up a new record-access identity via a DEFINE ACCESS method and returns its token.
AuthenticateAsync Task AuthenticateAsync(string token, CancellationToken cancellationToken = default) Authenticates the session with an existing JWT. Remembers the token for reconnect (clears any remembered credentials).
InvalidateAsync Task InvalidateAsync(CancellationToken cancellationToken = default) Invalidates the session's authentication; clears remembered token/credentials so a reconnect won't re-authenticate.
DisposeAsync ValueTask DisposeAsync() Idempotent. Disposes the owned transport (if the client created one) and the internal session semaphore. Does not dispose an externally supplied connection.

Reconnect behavior: when the transport is WebSocketConnection, the client wires its SessionRestore callback to re-run the same authenticate/sign-in + USE sequence automatically after a reconnect, using whichever of token or credentials was last set and the last-selected namespace/database.

CRUD

All CRUD methods delegate to the server's respective RPC method (create, select, update, upsert, merge, patch, delete, insert) via the type mapper for serialization/deserialization. RecordId-targeted single-record calls return T? (or throw, for CreateAsync); Table-targeted calls return IReadOnlyList<T> or Task for bulk/no-content operations.

Member Signature Behavior
CreateAsync<T> (table) Task<T> CreateAsync<T>(Table table, T content, CancellationToken cancellationToken = default) Creates a record with a server-generated id. Throws SurrealDbSerializationException if the server returns no created record.
CreateAsync<T> (id) Task<T> CreateAsync<T>(RecordId id, T content, CancellationToken cancellationToken = default) Creates a record with the given id. Same SurrealDbSerializationException guarantee.
InsertAsync<T> Task<IReadOnlyList<T>> InsertAsync<T>(Table table, IEnumerable<T> contents, CancellationToken cancellationToken = default) Bulk-inserts records via the insert RPC method and returns them as inserted.
SelectAsync<T> (table) Task<IReadOnlyList<T>> SelectAsync<T>(Table table, CancellationToken cancellationToken = default) Selects every record of a table.
SelectAsync<T> (id) Task<T?> SelectAsync<T>(RecordId id, CancellationToken cancellationToken = default) Selects one record; null if it doesn't exist.
UpdateAsync<T> Task<T?> UpdateAsync<T>(RecordId id, T content, CancellationToken cancellationToken = default) Replaces a record's content wholesale; null if absent. CLR null properties serialize as explicit NONE — they are set to NONE on the record, not omitted.
UpsertAsync<T> Task<T?> UpsertAsync<T>(RecordId id, T content, CancellationToken cancellationToken = default) Creates the record if absent, else replaces it; returns the resulting record.
MergeAsync<T> Task<T?> MergeAsync<T>(RecordId id, SurrealValue merge, CancellationToken cancellationToken = default) Merges fields from a SurrealValue object into the record (partial update). Merge fields whose value is NONE are set to NONE, not skipped. null if the record is absent.
PatchAsync<T> Task<T?> PatchAsync<T>(RecordId id, IReadOnlyList<SurrealPatchOperation> operations, CancellationToken cancellationToken = default) Applies RFC 6902 JSON Patch operations built via SurrealPatchOperation.Add/Remove/Replace/Copy/Move/Test. null if the record is absent.
DeleteAsync<T> (id) Task<T?> DeleteAsync<T>(RecordId id, CancellationToken cancellationToken = default) Deletes one record and returns it (the deleted content), or null if it didn't exist.
DeleteAsync (table) Task DeleteAsync(Table table, CancellationToken cancellationToken = default) Deletes every record of a table.

Table and RecordId are value types from Palm.SurrealDB.Net.Abstractions; construct a Table from a raw name (new Table("person") or the implicit string conversion) and a RecordId from a Table and a RecordIdKey (table.Id(RecordIdKey.From("ada")), or RecordId.Parse("person:ada")). Full reference: ../Palm.SurrealDB.Net.Abstractions/README.md.

Raw queries — QueryAsync

public Task<SurrealQueryResponse> QueryAsync(
    string surql,
    IReadOnlyDictionary<string, SurrealValue>? variables = null,
    CancellationToken cancellationToken = default)

Executes raw, parameterized SurrealQL text via the query RPC method and returns a SurrealQueryResponse — one SurrealStatementResult per statement in surql, in order. variables supplies $name parameter bindings; always use $name parameters, never string interpolation, to build dynamic queries. surql must be non-empty (ArgumentException.ThrowIfNullOrEmpty).

SurrealQueryResponse exposes:

  • IReadOnlyList<SurrealStatementResult> Statements — each with IsSuccess, Value (SurrealValue), Error (server message on failure), and Duration.
  • IReadOnlyList<T?> GetResults<T>(int statement = 0) — materializes a statement's results through the client's type mapper: an array result maps element-wise, a single value maps to a one-element list, NONE/NULL map to an empty list. Throws SurrealDbQueryException (with StatementIndex/ServerMessage) if that statement failed on the server, and ArgumentOutOfRangeException for an out-of-range statement index.
  • T? GetFirstOrDefault<T>(int statement = 0) — the first materialized result, or default.

For building queries programmatically instead of hand-writing SurrealQL text, use the AST and emitter in Palm.SurrealDB.Net.SurrealQL — never concatenate query strings.

LINQ — Query<T>()

Palm.SurrealDB.Net.Linq adds ISurrealClient.Query<T>(), an IQueryable<T> entry point that translates LINQ expression trees to SurrealQL (strict-throw — it never falls back to client-side evaluation of anything it can't translate):

using Palm.SurrealDB.Net.Linq;

var adults = await client.Query<Person>()
    .Where(p => p.Age >= 18)
    .OrderBy(p => p.Name)
    .Take(10)
    .ToListAsync();

Query<T>() itself is defined in Palm.SurrealDB.Net.Linq, not this package — add a reference to that project to use it. Full LINQ operator reference, translation rules, and the strict-throw contract: ../Palm.SurrealDB.Net.Linq/README.md.

Live queries — LiveAsync<T>

public IAsyncEnumerable<LiveQueryChange<T>> LiveAsync<T>(
    Table table, CancellationToken cancellationToken = default)

Starts a live query on table via the live RPC method and streams typed LiveQueryChange<T> values (Action, Id, Record) until the enumeration is cancelled or a LiveAction.Killed notification arrives. Requires the WebSocket transport — throws SurrealDbCapabilityException if the active connection doesn't support live queries (e.g. an HttpConnection). On normal disposal of the enumerator (not already killed, client not disposed), the client best-effort sends kill for the live query id; a SurrealDbException during that best-effort kill is swallowed since the live query dies with the session anyway.

await foreach (var change in client.LiveAsync<Person>(new Table("person"), cts.Token))
{
    switch (change.Action)
    {
        case LiveAction.Create:
        case LiveAction.Update:
            Console.WriteLine($"{change.Action}: {change.Record}");
            break;
        case LiveAction.Delete:
            Console.WriteLine($"Deleted: {change.Id}");
            break;
        case LiveAction.Killed:
            Console.WriteLine("Live query killed.");
            break;
    }
}

LiveQueryChange<T>.Record is null for LiveAction.Killed; for Delete it carries the full deleted record, materialized via the client's type mapper.

Session variables and functions

Member Signature Behavior
LetAsync Task LetAsync(string name, SurrealValue value, CancellationToken cancellationToken = default) Sets a session variable usable as $name in subsequent queries.
UnsetAsync Task UnsetAsync(string name, CancellationToken cancellationToken = default) Removes a session variable.
RunAsync Task<SurrealValue> RunAsync(string function, IReadOnlyList<SurrealValue>? arguments = null, CancellationToken cancellationToken = default) Runs a server-side function (e.g. "fn::my_func") with positional arguments and returns its result.
VersionAsync Task<string> VersionAsync(CancellationToken cancellationToken = default) Returns the server's version string.

TypeMapper

public ISurrealTypeMapper TypeMapper { get; }

Exposes the ISurrealTypeMapper the client was constructed with (or the default ReflectionSurrealTypeMapper) — the same instance used internally to serialize/deserialize CRUD payloads and query results. Useful for calling ToSurrealValue/FromSurrealValue directly, e.g. when building a MergeAsync payload or interpreting a raw SurrealValue.

Dependency injection — AddSurrealDb

SurrealDbServiceCollectionExtensions (static class) adds two AddSurrealDb overloads, both registering a singleton ISurrealClient via TryAddSingleton (a no-op if ISurrealClient is already registered):

// Direct options
public static IServiceCollection AddSurrealDb(
    this IServiceCollection services, SurrealDbOptions options)

// Factory-based — resolve options from the container (e.g. IConfiguration/IOptions)
public static IServiceCollection AddSurrealDb(
    this IServiceCollection services,
    Func<IServiceProvider, SurrealDbOptions> optionsFactory)

The direct-options overload calls options.Validate() eagerly, at registration time — an invalid SurrealDbOptions throws SurrealDbConfigurationException immediately during ConfigureServices, before the host even starts. The factory-based overload defers construction (and therefore validation) until the container first resolves ISurrealClient, since the factory itself may depend on other registered services.

SurrealDbOptions summary

SurrealDbOptions (Palm.SurrealDB.Net.Abstractions) is the primary configuration surface:

  • Uri Endpoint — required; scheme must be ws, wss, http, or https.
  • string? Namespace, string? Database — selected after connecting; Database requires Namespace.
  • SurrealCredentials? Credentials — RootCredentials / NamespaceCredentials / DatabaseCredentials / AccessCredentials / TokenCredentials.
  • TimeSpan ConnectTimeout (default 5s), TimeSpan RequestTimeout (default 30s) — both must be positive.
  • ReconnectOptions Reconnect — Enabled (default true), InitialDelay (200ms), MaxDelay (30s), MaxAttempts (null = unlimited), JitterFactor (0.25, must be in [0, 1]).
  • int LiveQueryChannelCapacity (default 1024) — bounded capacity of each live-query notification channel; must be at least 1.

Validate() throws SurrealDbConfigurationException for any violation above; both SurrealDbClient's constructor and the direct-options AddSurrealDb overload call it eagerly. Full reference (every member, defaults, and validation rule): ../Palm.SurrealDB.Net.Abstractions/README.md.

Error handling

Every exception type below derives from SurrealDbException (itself an Exception) and lives in Palm.SurrealDB.Net.Abstractions:

Exception Thrown by (in this package)
SurrealDbConfigurationException SurrealDbClient's constructor and AddSurrealDb(options) when SurrealDbOptions.Validate() rejects the options (bad endpoint scheme, Database without Namespace, non-positive timeouts, invalid reconnect settings, LiveQueryChannelCapacity < 1).
SurrealDbConnectionException Raised by the underlying transport (Palm.SurrealDB.Net.Protocol) when the connection fails or is lost; propagates through any client method that requires a live session. Carries IsRetriable indicating whether a retry (e.g. after reconnect) may succeed.
SurrealDbAuthenticationException Sign-in/authentication failures, including an unsupported SurrealCredentials subtype passed to a credentials-accepting method.
SurrealDbRpcException The server answers an RPC call with an error object; carries ErrorCode, Details, and Cause.
SurrealDbQueryException A statement inside a QueryAsync response failed server-side, surfaced when calling SurrealQueryResponse.GetResults<T>/GetFirstOrDefault<T>. Carries StatementIndex and ServerMessage.
SurrealDbSerializationException CreateAsync<T> when the server returns no created record; also raised by the type mapper for values that can't be serialized/deserialized.
SurrealDbCapabilityException LiveAsync<T> when the active transport (e.g. HttpConnection) doesn't support live queries.
ObjectDisposedException Any method called after DisposeAsync() has completed.

ArgumentNullException/ArgumentException (BCL types, not SurrealDbException subtypes) are thrown for straightforward null/empty argument violations (options, credentials, operations, contents, @namespace, token, name, function, surql) before any network call is attempted.

Worked examples

1. Connect and perform typed CRUD

using Palm.SurrealDB.Net;

public sealed record Person(string Name, int Age);

await using var client = new SurrealDbClient(new SurrealDbOptions
{
    Endpoint = new Uri("ws://127.0.0.1:8000/rpc"),
    Namespace = "test",
    Database = "test",
    Credentials = new RootCredentials("root", "root"),
});

await client.ConnectAsync();

var table = new Table("person");
var ada = await client.CreateAsync(table, new Person("Ada", 30));

var found = await client.SelectAsync<Person>(new RecordId(table, RecordIdKey.From("ada")));

var updated = await client.MergeAsync<Person>(
    new RecordId(table, RecordIdKey.From("ada")),
    SurrealValue.FromObject([new("age", SurrealValue.From(31))]));

var everyone = await client.SelectAsync<Person>(table);

var deleted = await client.DeleteAsync<Person>(new RecordId(table, RecordIdKey.From("ada")));

2. Raw QueryAsync with parameters

var response = await client.QueryAsync(
    "SELECT * FROM person WHERE age >= $minAge ORDER BY name",
    new Dictionary<string, SurrealValue> { ["minAge"] = SurrealValue.From(18) });

var adults = response.GetResults<Person>();      // statement 0
var firstAdult = response.GetFirstOrDefault<Person>();

3. DI registration in an ASP.NET Core / generic host Program.cs

using Palm.SurrealDB.Net;

var builder = WebApplication.CreateBuilder(args);

// Direct options — validated eagerly, at registration time.
builder.Services.AddSurrealDb(new SurrealDbOptions
{
    Endpoint = new Uri("ws://127.0.0.1:8000/rpc"),
    Namespace = "test",
    Database = "test",
    Credentials = new RootCredentials("root", "root"),
});

// Or: factory-based, resolving options from configuration.
builder.Services.AddSurrealDb(sp =>
{
    var config = sp.GetRequiredService<IConfiguration>();
    return new SurrealDbOptions
    {
        Endpoint = new Uri(config["SurrealDb:Endpoint"]!),
        Namespace = config["SurrealDb:Namespace"],
        Database = config["SurrealDb:Database"],
        Credentials = new RootCredentials(
            config["SurrealDb:User"]!, config["SurrealDb:Pass"]!),
    };
});

var app = builder.Build();

app.MapGet("/people", async (ISurrealClient client, CancellationToken ct) =>
    await client.SelectAsync<Person>(new Table("person"), ct));

await app.RunAsync();
Project Purpose
Palm.SurrealDB.Net.Abstractions Core value types and contracts this package depends on: RecordId, Table, SurrealValue, SurrealDbOptions, ISurrealClient, ISurrealTypeMapper, [SurrealTable]/[SurrealField].
Palm.SurrealDB.Net.Serialization The CBOR codec for SurrealValue and ReflectionSurrealTypeMapper (ISurrealTypeMapper's default implementation), the single source of truth for property↔field name mapping.
Palm.SurrealDB.Net.Protocol The WebSocket and HTTP RPC transports (WebSocketConnection, HttpConnection) this client selects between based on SurrealDbOptions.Endpoint.
Palm.SurrealDB.Net.SurrealQL The SurrealQL AST, parameterized emitter, parser, and formatter — the only supported path for building query text programmatically.
Palm.SurrealDB.Net.Linq The IQueryable<T> LINQ-to-SurrealQL provider behind client.Query<T>().
Palm.SurrealDB.FSharp The idiomatic F# DSL layered over the SurrealQL AST.
Root README Ecosystem overview, build/test commands, and phase status.
Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (5)

Showing the top 5 NuGet packages that depend on Palm.SurrealDB.Net:

Package Downloads
Palm.SurrealDB.Net.Linq

LINQ provider for SurrealDB — translates expression trees to SurrealQL via the Palm.SurrealDB.Net.SurrealQL AST.

Palm.SurrealDB.EntityFrameworkCore

Entity Framework Core provider for SurrealDB, built on the Palm.SurrealDB.Net client and SurrealQL AST.

Palm.SurrealDB.Net.AspNetCore

ASP.NET Core integration for SurrealDB — configuration-bound client registration, scoped request context, and bearer-token authentication handler for the Palm.SurrealDB ecosystem.

Palm.SurrealDB.Net.Aspire

.NET Aspire client integration component for SurrealDB — AddSurrealDbClient/AddKeyedSurrealDbClient, wiring health checks, tracing, metrics, and logging conventions for worker services.

Palm.SurrealDB.Net.Orleans

Microsoft Orleans grain-persistence provider backed by SurrealDB — queryable document grain state with optimistic concurrency, for the Palm.SurrealDB ecosystem.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.2.1 171 9/5/2026
0.2.0 170 9/3/2026