All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
3.0.0-alpha.4 - 2026-07-16
- Bump the embedded extension's
pyo3to 0.29.0 (andpyo3-async-runtimesto 0.29), fixing a high-severity out-of-bounds read inPyList/PyTupleiterators and a missingSyncbound onPyCFunction::new_closure. Affects the nativesurrealdb-embeddedpackage only; no API changes.
3.0.0-alpha.3 - 2026-07-16
Follow-up to 3.0.0-alpha.2: adds the into= row-model API, fixes session authentication propagation, aligns delete with select, and re-enables mypy type-checking on the test suite.
- Keyword-only
into=argument onselect,create,update,upsert,delete, andinsert(async and sync, including the session/transaction wrappers) mapping each returned record onto a model class — a dataclass, a pydanticBaseModel, or any class accepting the record's fields as keyword arguments. Return types are narrowed precisely per@overload: a single-record target resolves toModel(orModel | None), aTabletarget tolist[Model]. The no-data builder forms (create(record, into=Model),update(record, into=Model),insert(table, into=Model)) carry the model through their clause methods (.content/.merge/ … /.execute). Mapping reuses the existing_map_to_classhelper. query(sql).into(Model, rows=True)maps each row of a single statement's result ontoModel, returninglist[Model]. The default.into(cls)(statements-to-fields) behaviour is unchanged whenrowsis not set.
delete(RecordID)now returnsdict | None(the deleted record, orNonewhen absent), matchingselect;delete(Table)still returns a list.- Internal: the test suite is now type-checked by CI (
mypy tests/) with the full set of error codes enabled, so it guards against public-API type regressions. Onlyindex,union-attr, andcall-overloadremain suppressed for tests, because the publicValueunion is indexed into pervasively across the suite with no bug-catching value.
new_session()now propagates the connection's authentication to the new session, so session-scoped operations run with the connection's identity. Previously a freshly attached session was unauthenticated and its writes silently no-opped (the server returned an empty result with no error).authenticate()also records the connection's token so it can be replayed.
3.0.0-alpha.2 - 2026-07-16
Follow-up to 3.0.0-alpha.1 that finalises the v3 API surface and fixes a batch of issues found in review.
RecordID,Table,Duration,Range, and allGeometrytypes are now hashable, so they can be used asdictkeys andsetmembers.Datetimegained__eq__,__repr__, and__hash__, making it a proper value type..first()on the query builder (async coroutine / sync method), returning the first statement's result, orNonewhen there are no statements.- Exported
AsyncSurrealConnectionandBlockingSurrealConnectionunion type aliases for annotating connection instances. - HTTP connections implement
close()and reuse a single pooled session across requests within a context manager. - README "Live queries" section documenting
live()/subscribe_live()/kill(), and a pointer to the Spectron client. - Docstrings on the public CRUD / query / select / live-query methods.
- Breaking: Sync
select,create,update,upsert,delete, andinsertare now eager.select(...)anddelete(...)run immediately and return the result.create/update/upsert(record, data)andinsert(table, data)run immediately and return the result; the no-data form (create(record),insert(table)) returns a builder whose clause methods (.content/.replace/.merge/.patch/.relation) and.execute()run the operation and return the result. This removes the sync magic-consumption footguns (bool(),==, indexing,__getattr__) present in alpha.1. - Breaking: Sync builders no longer implement any auto-executing magic methods (
__bool__,__eq__,__getitem__,__iter__,__len__,__contains__,__getattr__, and the pending-repr/str). Inspecting a builder never triggers a query or mutation. - Breaking:
query()now always returns alist[Value](one entry per statement) — even for a single statement — for both async (await db.query(...)/.execute()) and sync (.execute()). This supersedes the alpha.1 "singleValuefor one statement,tuplefor many" behaviour. Use.first()for the first statement's result. - Breaking:
select(RecordID)(or a single"table:id"string) now returnsdict[str, Value] | None(the record, orNonewhen it is absent) instead of a single-element list;select(Table)(or a bare table name) still returnslist[Value].select()is now typed via@overloadon the templates, all four connections, and the session/transaction classes. - Breaking: The Spectron client is no longer re-exported from the top-level
surrealdbpackage; import it fromsurrealdb.spectron(from surrealdb.spectron import Spectron, AsyncSpectron). This keeps the Spectron surface out of the core SDK's stability guarantee. - Breaking:
query_raw's bound-variable keyword argument is renamed fromparamstovars, matchingquery. - The session and transaction wrapper classes now carry the same CRUD
@overloadprecision as the base connection. - Python
set/frozensetnow encode with SurrealDB's set tag (56) instead of the generic CBOR set tag (258). info()'s record-auth$authfallback is now applied consistently across all four transports and keyed on the structured error kind rather than the error-message text.
- Breaking: The low-level
TAG_*CBOR constants are no longer exported from the top-levelsurrealdbpackage; they remain available atsurrealdb.data.types.constants. - Removed the unused
AsyncSurrealDBMeta/BlockingSurrealDBMetametaclasses and the dead duplicatesurrealdb.cborshim modules (decoder,encoder,types,tool).
RecordID.parse(andtable_or_record_id) no longer raise on record ids containing:(e.g."user:complex:id").- WebSocket live queries:
subscribe_livegenerators are woken and cleaned up onkill()/close()instead of leaking; a mid-request disconnect raises a typedConnectionUnavailableErrorinstead of a bareKeyError; the blocking client correlates RPC replies by id (never returning a live notification as an RPC result), reads under its lock, and logs vialogginginstead ofprint. - Removed several unreachable encoder branches and a stale decoder
TODOin the CBOR layer.
3.0.0-alpha.1 - 2026-07-13
- New awaitable / lazy CRUD builder pattern.
create,update,upsert,delete, andinsertnow return a builder that exposes chainable clause methods (.content/.replace/.merge/.patch) and is awaitable (async) or auto-executing on consumption (sync). .insert(table, data, relation=True)(and the equivalent.insert(table).relation().content(data)chain) replaces the standaloneinsert_relationmethod.- New
run(name, args=None, version=None)method on every connection / session / transaction, wired to theRUNRPC method. query().into(cls)maps the N statement results positionally onto a dataclass (or any class accepting keyword arguments by parameter order).- New v3 API tests under
tests/unit_tests/connections/v3_api/covering builder clauses, multi-statement query results,.into(), andrun(). - Public re-exports of the builder classes (
AsyncCrudBuilder,AsyncInsertBuilder,AsyncQueryBuilder,AsyncQueryIntoBuilder, and theSync*equivalents) fromsurrealdb. QueryError.is_transaction_conflictproperty, detectingTRANSACTION_CONFLICTquery errors (#268).__str__methods forDatetime,Duration, and all 7Geometryclasses, rendering SurrealQL literals instead of Python's defaultrepr(#270).- Export
escape_identifierfrom the top-levelsurrealdbpackage and documentRecordID.id's raw (unescaped) contract (#271).
- Breaking:
query()now surfaces every statement result. A single-statement query returns the resultValue; a multi-statement query (orBEGIN ... COMMITblock) returnstuple[Value, ...]. Fixes the silent-discard behaviour reported in #232. - Breaking: Sync
query()/ CRUD methods return a lazy builder. The operation runs when the result is consumed (indexed, iterated, compared, printed, etc.) or when.execute()is called explicitly. Fire-and-forget statements likedb.query("DELETE foo")must call.execute()to run. - Breaking:
create,update,upsert,delete, andinsertare typed via@overloadso type checkers inferdict[str, Value]forRecordIDtargets andlist[Value]forTabletargets. - Breaking: Raw-string resource targets are now strictly validated against the safe-identifier pattern (
[A-Za-z_][A-Za-z0-9_]*). Names with hyphens or other special characters must be wrapped inTable(...)orRecordID(...)so the SDK can safely route them through parameter binding (CRUD ops usetype::table($var)) or SurrealQL's⟨...⟩escape (INSERT, where the server doesn't accepttype::table()). - The session and transaction classes (
AsyncSurrealSession/BlockingSurrealSession,AsyncSurrealTransaction/BlockingSurrealTransaction) expose the same builder API and forwardsession_id/txn_idthrough to every operation.
- Breaking:
db.merge(record, data)— usedb.update(record).merge(data)(or.create/.upsert(record).merge(data)). - Breaking:
db.patch(record, data)— usedb.update(record).patch(data). - Breaking:
db.insert_relation(table, data)— usedb.insert(table, data, relation=True)ordb.insert(table).relation().content(data).
- Restored the record-level auth fallback in
BlockingHttpSurrealConnection.info(). When the server returnedNo result foundfromINFO(record-auth scenario), the SDK now retries viaSELECT * FROM $authand returns the resolved record; the fallback was inadvertently dead in the initial v3 builder migration becausequery()returns a lazy builder. Range.__str__producing invalid SurrealQL (#269).
- Breaking: WebSocket
subscribe_live()now yields the full live-notification object (action,result,id, …) from the server instead of only the inner record (#247).
subscribe_liveWebSocket tests perform mutations on a secondary connection so query RPC replies are not interleaved with live notifications on the same socket.
2.0.0 - 2026-04-23
- Support
surrealkv+versioned://URL scheme for embedded databases with versioning (#231).
2.0.0-alpha.1 - 2026-02-25
- SurrealDB 3.x protocol and feature support (#230).
- Structured error hierarchy and
ServerErrorwith SurrealDB 3.x–style kind/details (#233). - Logfire observability example and README section (#229).
- Drop Python 3.9 support; minimum Python is 3.10 (#230).
- Add release-comment workflow for builds (#240).
- Fix WebSocket session and transaction ID handling for
begin/commit/cancel(#236).
1.0.8 - 2026-01-07
- Add optional
pydanticextra soRecordIDfields validate and serialize cleanly inBaseModels and JSON schema outputs.
- Changed
cerberusforpydantic-core.
- Improve build stability and ensure
musl-toolsis installed for Linux builds.
1.0.7 - 2025-12-03
- Support compound duration parsing in
Duration.parse. - Provide native embedded database support.
- Add comprehensive framework integration examples.
- Introduce
pyrightchecks and additional data type coverage. - Expand test coverage for record IDs and Cursor tooling rules.
- Simplify database method return types to
Value. - Issue text-based queries instead of using v1 RPC methods.
- Correct Duration encoding and decoding.
- Enforce GeoJSON-compliant closed linear rings in
GeometryPolygon. - Escape string identifiers in
RecordIDto match SurrealDB behavior. - Fix
decimal.Decimalencoding. - Address race condition in concurrent environments.
- Apply formatting, linting, and test stability fixes.
1.0.6 - 2025-07-21
- Switch project management to
uvand simplify the developer environment.
1.0.5 - 2025-07-18
- Streamline CI/build workflows and improve developer tooling.
1.0.4 - 2025-05-21
- Add decimal support and CBOR integration improvements.
- Improve polygon handling and async WebSocket error handling.
- Fix
Noneencoding/decoding for SurrealDB v2.2.x and later. - Correct timezone offset decoding and types in connections.
- Normalize error response handling.
1.0.3 - 2025-02-04
- Correct datetime tagging.
1.0.2 - 2025-02-02
- Update project metadata.
- Remove WebSocket max message size limit.
1.0.1 - 2025-02-01
- Resolve signup/signin issues and improve CI/test stability.
- Initial stable release of the SurrealDB Python client.