convex_sync_types

Shared Rust types for Convex's WebSocket sync protocol: validated identifiers, module/UDF paths, query and session IDs, timestamps, and client/server message enums.

Library
Cargo
v0.10.4
90stars
Apache License 2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
61/100Good
Development Activity84
Maintenance40
Community48
Maturity52
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
63/100Good
Architecture78
Code Quality68
Innovation52
Learning Curve55

convex_sync_types is the foundational Rust crate behind the official convex client library, defining the wire types that flow over Convex’s WebSocket sync protocol. It provides strongly typed, serde-serializable representations of client and server messages, query and mutation identifiers, module and UDF (user-defined function) paths, session tracking, admin authorization headers, and transaction timestamps.

Rather than passing loosely typed JSON between a Rust client and a Convex backend, the crate encodes the protocol’s invariants directly in the type system: identifiers are validated against Convex’s naming rules at parse time, module paths distinguish system code, dependencies, HTTP routes, and cron jobs from ordinary user modules, and timestamps model Convex’s nanosecond transaction clock with checked successor arithmetic. It has no networking code of its own — it is a pure data-modeling layer that the convex crate, and anyone implementing the sync protocol independently, builds on top of.

What You Get

  • Protocol message enums - ClientMessage and ServerMessage model every message exchanged over the Convex sync WebSocket (connect, query-set modifications, mutations, actions, transitions, chunked transitions, auth errors, fatal errors), ready for serde JSON (de)serialization.
  • Validated identifiers - check_valid_identifier/is_valid_identifier and PathComponent/ModulePath/UdfPath types enforce Convex’s naming rules for table names, field names, and function paths at construction time rather than at the server.
  • Query and session tracking types - QueryId, QuerySetVersion, SessionId, and SessionRequestSeqNumber types track live query subscriptions and client sessions across reconnects.
  • Nanosecond transaction timestamps - a Timestamp type representing Convex’s monotonic transaction clock, with MIN/MAX bounds and checked successor/comparison helpers.
  • Jittered exponential backoff - a Backoff struct implementing AWS-style exponential-backoff-with-jitter, used for WebSocket reconnect scheduling.
  • Admin auth header types - ConvexAdminAuthorization implements the headers crate’s Credentials trait to encode/decode the Convex <key> admin authorization scheme, plus deprecation-header constants.

Common Use Cases

  • Building a custom Convex client - implementers writing a Convex client for a runtime not covered by the official SDKs, or a lower-level Rust integration, reuse these types instead of re-deriving the sync protocol’s message shapes from scratch.
  • Validating table and function names before submission - a Rust backend or tool validates user-supplied table, field, or function-path names against Convex’s identifier rules before sending them to a deployment.
  • Reconnect and backoff logic for WebSocket clients - a long-lived Rust service embedding Convex connectivity uses Backoff to implement jittered reconnect delays consistent with the official client’s behavior.
  • Testing protocol serialization - projects that serialize or deserialize Convex sync messages in tests enable the testing feature to get proptest-backed arbitrary implementations for round-trip and property-based testing.

Under The Hood

Architecture The crate is a pure type-modeling layer with no I/O: lib.rs re-exports a small public surface (FunctionName, ModulePath/CanonicalizedModulePath, Timestamp, UdfPath/CanonicalizedUdfPath, and the protocol types from types/mod.rs) built from independent modules — identifier.rs for name validation, path.rs for path components, module_path.rs and udf_path.rs for function addressing, timestamp.rs for the transaction clock, backoff.rs for reconnect scheduling, and headers.rs for admin auth encoding. types/mod.rs and types/json.rs (over 1,700 lines combined) define the ClientMessage/ServerMessage enums and their custom serde Serialize/Deserialize implementations, hand-written rather than derived so the wire format can diverge from Rust’s natural enum representation and stay compatible with the JS/TS sync protocol. There is a strict layering: path underpins module_path, which underpins udf_path, which the top-level types module composes into protocol messages — a change to the wire format’s addressing scheme would ripple upward through exactly that chain.

Tech Stack Pure Rust with serde/serde_json (with float_roundtrip, preserve_order, raw_value features) for wire serialization, derive_more for boilerplate trait derives (From, Into, Display, Deref, FromStr), strum for enum string conversions, uuid for session identifiers, headers for typed HTTP header encoding, and anyhow for fallible construction. proptest/proptest-derive are optional dependencies gated behind a testing feature flag, used only for property-based test generation. The crate has no async runtime, HTTP client, or WebSocket dependency of its own — those live in the parent convex crate that depends on this one via a workspace path dependency.

Code Quality Inline #[cfg(test)] mod tests blocks exist in identifier.rs, module_path.rs, and udf_path.rs covering parsing and validation edge cases, several using proptest for property-based round-trip checks. Error handling is consistently anyhow::Result with descriptive bail! messages for invalid input rather than panics. Types favor newtype wrappers with restricted constructors (FromStr, private inner fields) to make invalid states hard to represent. No CI workflow files are present in this shallow clone, and a #[cfg(feature = "testing")] pub mod testing referenced from lib.rs and used by property tests was not present in this checkout, though that likely reflects the shallow single-commit clone rather than the published crate.

API Design The public API is deliberately narrow — lib.rs re-exports a curated set of types rather than exposing every internal module publicly, keeping the surface stable even as internal message-handling code evolves. Constructors validate at the boundary (FromStr impls for PathComponent, ModulePath, UdfPath return Result rather than silently accepting malformed input), so callers get parse errors early instead of protocol errors later. Getting started requires only adding the crate and matching on ClientMessage/ServerMessage variants or constructing identifiers via FromStr — there is no setup, configuration, or async runtime dependency to pull in.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search