Skip to main content

sqlx_cloudflare_do/
lib.rs

1//! A Cloudflare Durable Objects driver for sqlx.
2//!
3//! [`Do`] is an [`sqlx::Database`](sqlx_core::database::Database) over a
4//! Durable Object's SQL storage, so code written against sqlx's runtime API --
5//! `sqlx::query`, `sqlx::query_as`, `#[derive(sqlx::FromRow)]`, `Executor` --
6//! runs inside a SQLite-backed Durable Object with the database type swapped.
7//!
8//! ```no_run
9//! use sqlx_cloudflare_do::{Do, DoConnection};
10//! use worker::{durable_object, DurableObject, Env, Request, Response, State};
11//!
12//! #[derive(sqlx::FromRow)]
13//! struct User {
14//!     id: i64,
15//!     name: String,
16//! }
17//!
18//! #[durable_object]
19//! pub struct Users {
20//!     conn: DoConnection,
21//! }
22//!
23//! impl DurableObject for Users {
24//!     fn new(state: State, _env: Env) -> Self {
25//!         // One connection for the object's lifetime: it is only a handle to
26//!         // storage the object already owns.
27//!         Self { conn: DoConnection::new(state.storage()) }
28//!     }
29//!
30//!     async fn fetch(&self, _req: Request) -> worker::Result<Response> {
31//!         let user = sqlx::query_as::<Do, User>("SELECT id, name FROM users WHERE id = ?")
32//!             .bind(1_i64)
33//!             .fetch_one(&self.conn)
34//!             .await
35//!             .map_err(|e| worker::Error::RustError(e.to_string()))?;
36//!
37//!         Response::ok(user.name)
38//!     }
39//! }
40//! ```
41//!
42//! The database lives in the Durable Object: queries never leave the process,
43//! and each one runs to completion before its future first returns. The class
44//! must be declared with `new_sqlite_classes` in `wrangler.toml` -- the
45//! key-value-backed kind has no SQL storage.
46//!
47//! # What Durable Object storage cannot do
48//!
49//! Each of these fails loudly rather than pretending:
50//!
51//! - **`begin()`.** It returns an error: `sql.exec` rejects `BEGIN` and
52//!   `SAVEPOINT`. Transactions take a callback instead --
53//!   [`DoConnection::transaction`] -- and [`DoConnection::execute_batch`] runs
54//!   several statements atomically without one.
55//! - **Integers beyond ±(2^53 − 1).** Values cross as JavaScript numbers, so
56//!   binding a wider `i64` is an encode error rather than a rounded value.
57//!   See [`types`].
58//! - **`query!` and `describe`.** sqlx's macros only know its built-in
59//!   drivers, and `sql.exec` cannot describe a statement without running it.
60//!
61//! There is no `sqlx::Pool` either -- a Durable Object's storage has nothing
62//! to pool -- and no `sqlx::migrate!`.
63//!
64//! This crate only *runs* on `wasm32-unknown-unknown`, inside a Worker. It
65//! compiles on other targets so that docs, unit tests and `cargo check` work,
66//! but its `Send` impls lean on Durable Objects being single-threaded: do not
67//! call it from a threaded host.
68
69mod arguments;
70mod batch;
71mod column;
72mod connection;
73mod database;
74mod error;
75mod executor;
76mod js;
77mod row;
78mod statement;
79mod transaction;
80pub mod types;
81mod value;
82
83pub use arguments::DoArguments;
84pub use column::DoColumn;
85pub use connection::{DoConnectOptions, DoConnection};
86pub use database::Do;
87pub use row::DoRow;
88pub use statement::DoStatement;
89pub use transaction::{DoTransaction, DoTransactionManager};
90pub use value::{DoValue, DoValueRef};
91
92// The types Durable Object storage shares with D1, under this crate's names:
93// both are SQLite behind a JavaScript API, so they are the same types
94// underneath.
95#[doc(inline)]
96pub use sqlx_cloudflare_core::{
97    DatabaseError as DoDatabaseError, QueryResult as DoQueryResult, TypeInfo as DoTypeInfo,
98    Value as DoArgumentValue,
99};
100
101/// What one statement of a [`DoConnection::fetch_batch`] did, and the rows it
102/// returned.
103pub type DoBatchResult = sqlx_cloudflare_core::BatchResult<DoRow>;
104
105/// An executor for Durable Object storage: `&DoConnection` or
106/// `&mut DoConnection`.
107pub trait DoExecutor<'c>: sqlx_core::executor::Executor<'c, Database = Do> {}
108impl<'c, T: sqlx_core::executor::Executor<'c, Database = Do>> DoExecutor<'c> for T {}
109
110// NOTE: required due to the lack of lazy normalization
111sqlx_core::impl_into_arguments_for_arguments!(DoArguments);
112sqlx_core::impl_column_index_for_row!(DoRow);
113sqlx_core::impl_column_index_for_statement!(DoStatement);
114sqlx_core::impl_acquire!(Do, DoConnection);
115sqlx_core::impl_encode_for_option!(Do);