From 580fc15f6474e7ac4f075cb32958f5ca6b0181fa Mon Sep 17 00:00:00 2001 From: Phoebe Goldman Date: Tue, 25 Nov 2025 16:41:11 -0500 Subject: [PATCH 1/9] Add docs for procedures In the spirit of our planned move to concept-based documentation rather than language-based documentation, I've chosen to add a quick section to the overview, and then a new page for documentation about procedures. I have not updated any tutorials or reference pages. --- docs/docs/01-Intro/01-overview.md | 109 ++++++ docs/docs/11-Procedures/01-overview.md | 353 ++++++++++++++++++ .../01-incremental-migrations.md | 0 .../02-reject-client-connections.md | 0 .../03-using-auth-claims.md | 0 .../01-overview.md | 0 .../02-creating-a-project.md | 0 .../03-configuring-a-project.md | 0 .../04-testing.md | 0 .../05-react-integration.md | 0 .../01-authorization.md | 0 .../02-identity.md | 0 .../03-database.md | 0 .../01-module-abi-reference.md | 0 .../02-sats-json.md | 0 .../03-bsatn.md | 0 .../{15-Appendix => 16-Appendix}/index.md | 0 17 files changed, 462 insertions(+) create mode 100644 docs/docs/11-Procedures/01-overview.md rename docs/docs/{11-How-To => 12-How-To}/01-incremental-migrations.md (100%) rename docs/docs/{11-How-To => 12-How-To}/02-reject-client-connections.md (100%) rename docs/docs/{11-How-To => 12-How-To}/03-using-auth-claims.md (100%) rename docs/docs/{12-SpacetimeAuth => 13-SpacetimeAuth}/01-overview.md (100%) rename docs/docs/{12-SpacetimeAuth => 13-SpacetimeAuth}/02-creating-a-project.md (100%) rename docs/docs/{12-SpacetimeAuth => 13-SpacetimeAuth}/03-configuring-a-project.md (100%) rename docs/docs/{12-SpacetimeAuth => 13-SpacetimeAuth}/04-testing.md (100%) rename docs/docs/{12-SpacetimeAuth => 13-SpacetimeAuth}/05-react-integration.md (100%) rename docs/docs/{13-HTTP API => 14-HTTP API}/01-authorization.md (100%) rename docs/docs/{13-HTTP API => 14-HTTP API}/02-identity.md (100%) rename docs/docs/{13-HTTP API => 14-HTTP API}/03-database.md (100%) rename docs/docs/{14-Internals => 15-Internals}/01-module-abi-reference.md (100%) rename docs/docs/{14-Internals => 15-Internals}/02-sats-json.md (100%) rename docs/docs/{14-Internals => 15-Internals}/03-bsatn.md (100%) rename docs/docs/{15-Appendix => 16-Appendix}/index.md (100%) diff --git a/docs/docs/01-Intro/01-overview.md b/docs/docs/01-Intro/01-overview.md index 35bfb5d0966..dab35675478 100644 --- a/docs/docs/01-Intro/01-overview.md +++ b/docs/docs/01-Intro/01-overview.md @@ -279,6 +279,115 @@ or at a specific time. +### Procedure + +A **procedure** is a function exported by a [database](#database), similar to a [reducer](#reducer). +Connected [clients](#client-side-sdks) can call procedures. +Procedures can perform additional operations not possible in reducers, including making HTTP requests to external services. +However, procedures don't automatically run in database transactions, +and must manually open and commit a transaction in order to read from or modify the database state. + + + + +A procedure can be defined in a Rust module: + +```rust +#[spacetimedb::procedure] +pub fn make_request(ctx: &mut spacetimedb::ProcedureContext) -> String { + // ... +} +``` + +And a Rust [client](#client) can call that procedure: + +```rust +fn main() { + // ...setup code, then... + ctx.procedures.make_request(); +} +``` + +A Rust [client](#client) can also register a callback to run when a procedure call finishes, which will be invoked with that procedure's return value: + +```rust +fn main() { + // ...setup code, then... + ctx.procedures.make_request_then(|ctx, res| { + match res { + Ok(string) => log::info!("Procedure `make_request` returned {string}"), + Err(e) => log::error!("Procedure `make_request` failed! {e:?}"), + } + }) +} +``` + + + + +C# modules currently cannot define procedures. Support for defining procedures in C# modules will be released shortly. + +A C# [client](#client) can call a procedure defined by a Rust or TypeScript module: + +```csharp +void Main() +{ + // ...setup code, then... + ctx.Procedures.MakeRequest(); +} +``` + +A C# [client](#client) can also register a callback to run when a procedure call finishes, which will be invoked with that procedure's return value: + +```csharp +void Main() +{ + // ...setup code, then... + ctx.Procedures.MakeRequestThen((ctx, res) => + { + if (res.IsSuccess) + { + Log.Debug("Procedure `make_request` returned {Value}", res.Value); + } + else + { + throw new Exception(res.Error, "Procedure `make_request` failed!"); + } + }); +} +``` + + + + +A procedure can be defined in a TypeScript module: + +```typescript +spacetimedb.procedure("make_request", t.string(), ctx => { + // ... +}) +``` + +And a TypeScript [client](#client) can call that procedure: + +```typescript +ctx.procedures.makeRequest(); +``` + +A Rust [client](#client) can also register a callback to run when a procedure call finishes, which will be invoked with that procedure's return value: + +```typescript +ctx.procedures.makeRequest().then( + res => console.log(`Procedure make_request returned ${res}`), + err => console.error(`Procedure make_request failed! ${err}`), +); +``` + + + + +See [Procedures](/procedures) for more details about procedures. + ### Client A **client** is an application that connects to a [database](#database). A client logs in using an [identity](#identity) and receives an [connection id](#connectionid) to identify the connection. After that, it can call [reducers](#reducer) and query public [tables](#table). diff --git a/docs/docs/11-Procedures/01-overview.md b/docs/docs/11-Procedures/01-overview.md new file mode 100644 index 00000000000..2c629a4de4c --- /dev/null +++ b/docs/docs/11-Procedures/01-overview.md @@ -0,0 +1,353 @@ +--- +Title: Procedures +slug: /procedures +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +A **procedure** is a function exported by a [database](/#database), similar to a [reducer](/#reducer). +Connected [clients](/#client-side-sdks) can call procedures. +Procedures can perform additional operations not possible in reducers, including making HTTP requests to external services. +However, procedures don't automatically run in database transactions, +and must manually open and commit a transaction in order to read from or modify the database state. +For this reason, prefer defining reducers rather than procedures unless you need to use one of the special procedure operators. + +# Defining Procedures + + + + +Define a procedure by annotating a function with `#[spacetimedb::procedure]`. + +This function's first argument must be of type `&mut spacetimedb::ProcedureContext`. +By convention, this argument is named `ctx`. + +A procedure may accept any number of additional arguments. +Each argument must be of a type that implements `spacetimedb::SpacetimeType`. +When defining a `struct` or `enum`, annotate it with `#[derive(spacetimedb::SpacetimeType)]` +to make it usable as a procedure argument. +These argument values will not be broadcast to clients other than the caller. + +A procedure may return a value of any type that implements `spacetimedb::SpacetimeType`. +This return value will be sent to the caller, but will not be broadcast to any other clients. + +```rust +#[spacetimedb::procedure] +fn add_two_numbers(ctx: &mut spacetimedb::ProcedureContext, lhs: u32, rhs: u32) -> u64 { + lhs as u64 + rhs as u64 +} +``` + + + + +Support for procedures in C# modules is coming soon! + + + + +TODO(noa) + + + + +## Accessing the database + + + + +Unlike reducers, procedures don't automatically run in database transactions. +This means there's no `ctx.db` field to access the database. +Instead, procedure code must manage transactions explicitly with `ProcedureContext::with_tx`. + +```rust +#[spacetimedb::table(name = my_table)] +struct MyTable { + a: u32, + b: String, +} + +#[spacetimedb::procedure] +fn insert_a_value(ctx: &mut ProcedureContext, a: u32, b: String) { + ctx.with_tx(|ctx| { + ctx.my_table().insert(MyTable { a, b }); + }); +} +``` + +`ProcedureContext::with_tx` takes a function of type `Fn(&TxContext) -> T`. +Within that function, the `&TxContext` can be used to access the database +[in all the same ways as a `ReducerContext`](https://docs.rs/spacetimedb/latest/spacetimedb/struct.ReducerContext.html). +When the function returns, the transaction will be committed, +and its changes to the database state will become permanent and be broadcast to clients. +If the function panics, the transaction will be rolled back, and its changes will be discarded. +However, for transactions that may fail, +[prefer calling `try_with_tx` and returning a `Result`](#fallible-database-operations) rather than panicking. + +:::warning +The function passed to `ProcedureContext::with_tx` may be invoked multiple times, +possibly seeing a different version of the database state each time. + +If invoked more than once with reference to the same database state, +it must perform the same operations and return the same result each time. + +If invoked more than once with reference to different database states, +values observed during prior runs must not influence the behavior of the function or the calling procedure. + +Avoid capturing mutable state within functions passed to `with_tx`. +::: + + + + +TODO(noa) + + + + +### Fallible database operations + + + + +For fallible database operations, instead use `ProcedureContext::try_with_tx`: + +```rust +#[spacetimedb::procedure] +fn maybe_insert_a_value(ctx: &mut ProcedureContext, a: u32, b: String) { + ctx.try_with_tx(|ctx| { + if a < 10 { + return Err("a is less than 10!"); + } + ctx.my_table().insert(MyTable { a, b }); + Ok(()) + }); +} +``` + +`ProcedureContext::try_with_tx` takes a function of type `Fn(&TxContext) -> Result`. +If the function returns `Ok`, the transaction will be committed, +and its changes to the database state will become permanent and be broadcast to clients. +If that function returns `Err`, the transaction will be rolled back, and its changes will be discarded. + + + + +TODO(noa) + + + + +### Reading values out of the database + + + + +Functions passed to +[`ProcedureContext::with_tx`](#accessing-the-database) and [`ProcedureContext::try_with_tx`](#fallible-database-operations) +may return a value, and that value will be returned to the calling procedure. + +Transaction return values are never saved or broadcast to clients, and are used only by the calling procedure. + +```rust +#[spacetimedb::table(name = player)] +struct Player { + id: spacetimedb::Identity, + level: u32, +} + +#[spacetimedb::procedure] +fn find_highest_level_player(ctx: &mut ProcedureContext) { + let highest_level_player = ctx.with_tx(|ctx| { + ctx.db.player().iter().max_by_key(|player| player.level) + }); + match highest_level_player { + Some(player) => log::info!("Congratulations to {}", player.id), + None => log::warn!("No players..."), + } +} +``` + + + + +TODO(noa) + + + + +## HTTP Requests + + + + +Procedures can make HTTP requests to external services using methods contained in `ctx.http`. + +`ctx.http.get` performs simple `GET` requests with no headers: + +```rust +#[spacetimedb::procedure] +fn get_request(ctx: &mut ProcedureContext) { + match ctx.http.get("https://example.invalid") { + Ok(response) => log::info!( + "Got response with status {} and body {}", + response.status(), + response.body().into_string_lossy(), + ), + Err(error) => log::error!("Request failed: {error:?}"), + } +} +``` + +`ctx.http.send` sends any [`http::Request`](https://docs.rs/http/latest/http/request/struct.Request.html) +whose body can be converted to `spacetimedb::http::Body`. +`http::Request` is re-exported as `spacetimedb::http::Request`. + +```rust +#[spacetimedb::procedure] +fn post_request(ctx: &mut spacetimedb::ProcedureContext) { + let request = spacetimedb::http::Request::builder() + .uri("https://example.invalid/upload") + .method("POST") + .header("Content-Type", "text/plain") + .body("This is the body of the HTTP request") + .expect("Building `Request` object failed"); + match ctx.http.send(request) { + Ok(response) => log::info!( + "Got response with status {} and body {}", + response.status(), + response.body().into_string_lossy(), + ), + Err(error) => log::error!("Request failed: {error:?}"), + } +} +``` + +Each of these methods returns a [`http::Response`](https://docs.rs/http/latest/http/response/struct.Response.html#method.body) +containing a `spacetimedb::http::Body`. `http::Response` is re-exported as `spacetimedb::http::Response`. + +Set a timeout for a `ctx.http.send` request by including a `spacetimedb::http::Timeout` as an [`extension`](https://docs.rs/http/latest/http/request/struct.Builder.html#method.extension): + +```rust +#[spacetimedb::procedure] +fn get_request_with_short_timeout(ctx: &mut spacetimedb::ProcedureContext) { + let request = spacetimedb::http::Request::builder() + .uri("https://example.invalid") + .method("GET") + .extension(spacetimedb::http::Timeout(std::time::Duration::from_millis(10))) + .expect("Building `Request` object failed"); + ctx.http.send(request).expect("HTTP request failed"); +} +``` + +Procedures can't send requests at the same time as holding open a [transaction](#accessing-the-database). + + + + +TODO(noa) + + + + +# Calling procedures + + + + +Clients can invoke procedures using methods on `ctx.procedures`: + +```rust +ctx.procedures.insert_a_value(12, "Foo".to_string()); +``` + + + + +Clients can invoke procedures using methods on `ctx.Procedures`: + +```csharp +ctx.Procedures.InsertAValue(12, "Foo"); +``` + + + + +Clients can invoke procedures using methods on `ctx.procedures`: + +```typescript +ctx.procedures.insertAValue({ a: 12, b: "Foo" }); +``` + + + + +Clients can invoke procedures using methods on `Ctx->Procedures`: + +```cpp +Ctx->Procedures->InsertAValue(12, "Foo"); +``` + + + + +## Observing return values + + + + +A client can also invoke a procedure while registering a callback to run when it completes. +That callback will have access to the return value of the procedure, +or an error if the procedure fails. + +```rust +ctx.procedures.add_two_numbers_then(1, 2, |ctx, result| { + let sum = result.expect("Procedure failed"); + println!("1 + 2 = {sum}"); +}); +``` + + + + +A client can also invoke a procedure while registering a callback to run when it completes. +That callback will have access to the return value of the procedure, +or an error if the procedure fails. + +```csharp +ctx.Procedures.AddTwoNumbers(12, "Foo", (ctx, result) => +{ + if (result.IsSuccess) + { + Log.Info($"1 + 2 = {result.Value!}"); + } + else + { + throw result.Error!; + } +}); +``` + + + + +When a client invokes a procedure, it gets a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) which resolves to the return value of the procedure. + +```typescript +ctx.procedures.addTwoNumbers({ lhs: 1, rhs: 2 }).then( + sum => console.log(`1 + 2 = {sum}`) +); +``` + + + + +A client can also invoke a procedure while registering a callback to run when it completes. +That callback will have access to the return value of the procedure, +or an error if the procedure fails. + +TODO + + + diff --git a/docs/docs/11-How-To/01-incremental-migrations.md b/docs/docs/12-How-To/01-incremental-migrations.md similarity index 100% rename from docs/docs/11-How-To/01-incremental-migrations.md rename to docs/docs/12-How-To/01-incremental-migrations.md diff --git a/docs/docs/11-How-To/02-reject-client-connections.md b/docs/docs/12-How-To/02-reject-client-connections.md similarity index 100% rename from docs/docs/11-How-To/02-reject-client-connections.md rename to docs/docs/12-How-To/02-reject-client-connections.md diff --git a/docs/docs/11-How-To/03-using-auth-claims.md b/docs/docs/12-How-To/03-using-auth-claims.md similarity index 100% rename from docs/docs/11-How-To/03-using-auth-claims.md rename to docs/docs/12-How-To/03-using-auth-claims.md diff --git a/docs/docs/12-SpacetimeAuth/01-overview.md b/docs/docs/13-SpacetimeAuth/01-overview.md similarity index 100% rename from docs/docs/12-SpacetimeAuth/01-overview.md rename to docs/docs/13-SpacetimeAuth/01-overview.md diff --git a/docs/docs/12-SpacetimeAuth/02-creating-a-project.md b/docs/docs/13-SpacetimeAuth/02-creating-a-project.md similarity index 100% rename from docs/docs/12-SpacetimeAuth/02-creating-a-project.md rename to docs/docs/13-SpacetimeAuth/02-creating-a-project.md diff --git a/docs/docs/12-SpacetimeAuth/03-configuring-a-project.md b/docs/docs/13-SpacetimeAuth/03-configuring-a-project.md similarity index 100% rename from docs/docs/12-SpacetimeAuth/03-configuring-a-project.md rename to docs/docs/13-SpacetimeAuth/03-configuring-a-project.md diff --git a/docs/docs/12-SpacetimeAuth/04-testing.md b/docs/docs/13-SpacetimeAuth/04-testing.md similarity index 100% rename from docs/docs/12-SpacetimeAuth/04-testing.md rename to docs/docs/13-SpacetimeAuth/04-testing.md diff --git a/docs/docs/12-SpacetimeAuth/05-react-integration.md b/docs/docs/13-SpacetimeAuth/05-react-integration.md similarity index 100% rename from docs/docs/12-SpacetimeAuth/05-react-integration.md rename to docs/docs/13-SpacetimeAuth/05-react-integration.md diff --git a/docs/docs/13-HTTP API/01-authorization.md b/docs/docs/14-HTTP API/01-authorization.md similarity index 100% rename from docs/docs/13-HTTP API/01-authorization.md rename to docs/docs/14-HTTP API/01-authorization.md diff --git a/docs/docs/13-HTTP API/02-identity.md b/docs/docs/14-HTTP API/02-identity.md similarity index 100% rename from docs/docs/13-HTTP API/02-identity.md rename to docs/docs/14-HTTP API/02-identity.md diff --git a/docs/docs/13-HTTP API/03-database.md b/docs/docs/14-HTTP API/03-database.md similarity index 100% rename from docs/docs/13-HTTP API/03-database.md rename to docs/docs/14-HTTP API/03-database.md diff --git a/docs/docs/14-Internals/01-module-abi-reference.md b/docs/docs/15-Internals/01-module-abi-reference.md similarity index 100% rename from docs/docs/14-Internals/01-module-abi-reference.md rename to docs/docs/15-Internals/01-module-abi-reference.md diff --git a/docs/docs/14-Internals/02-sats-json.md b/docs/docs/15-Internals/02-sats-json.md similarity index 100% rename from docs/docs/14-Internals/02-sats-json.md rename to docs/docs/15-Internals/02-sats-json.md diff --git a/docs/docs/14-Internals/03-bsatn.md b/docs/docs/15-Internals/03-bsatn.md similarity index 100% rename from docs/docs/14-Internals/03-bsatn.md rename to docs/docs/15-Internals/03-bsatn.md diff --git a/docs/docs/15-Appendix/index.md b/docs/docs/16-Appendix/index.md similarity index 100% rename from docs/docs/15-Appendix/index.md rename to docs/docs/16-Appendix/index.md From 56a7ca64eec120adaf52b8097d1cc0912f496367 Mon Sep 17 00:00:00 2001 From: Phoebe Goldman Date: Tue, 25 Nov 2025 16:47:33 -0500 Subject: [PATCH 2/9] fix typo: it's not "grouId" --- docs/docs/01-Intro/01-overview.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/01-Intro/01-overview.md b/docs/docs/01-Intro/01-overview.md index dab35675478..b023304b20f 100644 --- a/docs/docs/01-Intro/01-overview.md +++ b/docs/docs/01-Intro/01-overview.md @@ -287,7 +287,7 @@ Procedures can perform additional operations not possible in reducers, including However, procedures don't automatically run in database transactions, and must manually open and commit a transaction in order to read from or modify the database state. - + A procedure can be defined in a Rust module: From 9210f65d5b5311eaad593e21222ea9db65f06a90 Mon Sep 17 00:00:00 2001 From: Phoebe Goldman Date: Tue, 25 Nov 2025 16:51:37 -0500 Subject: [PATCH 3/9] Add hedging about procedures being unstable --- docs/docs/01-Intro/01-overview.md | 11 ++++++++++- docs/docs/11-Procedures/01-overview.md | 9 +++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/docs/docs/01-Intro/01-overview.md b/docs/docs/01-Intro/01-overview.md index b023304b20f..eed4c5c1d70 100644 --- a/docs/docs/01-Intro/01-overview.md +++ b/docs/docs/01-Intro/01-overview.md @@ -287,10 +287,19 @@ Procedures can perform additional operations not possible in reducers, including However, procedures don't automatically run in database transactions, and must manually open and commit a transaction in order to read from or modify the database state. +Procedures are currently in beta, and their API may change in upcoming SpacetimeDB releases. + -A procedure can be defined in a Rust module: +Because procedures are unstable, Rust modules that define them must opt in to the `unstable` feature in their `Cargo.toml`: + +```toml +[dependencies] +spacetimedb = { version = "1.x", features = ["unstable"] } +``` + +Then, that module can define a procedure: ```rust #[spacetimedb::procedure] diff --git a/docs/docs/11-Procedures/01-overview.md b/docs/docs/11-Procedures/01-overview.md index 2c629a4de4c..dd63f34b578 100644 --- a/docs/docs/11-Procedures/01-overview.md +++ b/docs/docs/11-Procedures/01-overview.md @@ -13,11 +13,20 @@ However, procedures don't automatically run in database transactions, and must manually open and commit a transaction in order to read from or modify the database state. For this reason, prefer defining reducers rather than procedures unless you need to use one of the special procedure operators. +Procedures are currently in beta, and their API may change in upcoming SpacetimeDB releases. + # Defining Procedures +Because procedures are unstable, Rust modules that define them must opt in to the `unstable` feature in their `Cargo.toml`: + +```toml +[dependencies] +spacetimedb = { version = "1.x", features = ["unstable"] } +``` + Define a procedure by annotating a function with `#[spacetimedb::procedure]`. This function's first argument must be of type `&mut spacetimedb::ProcedureContext`. From d151889707edc5cb19d1f0fa6c0af20b9f9cac97 Mon Sep 17 00:00:00 2001 From: Phoebe Goldman Date: Wed, 26 Nov 2025 11:45:29 -0500 Subject: [PATCH 4/9] Address review --- docs/docs/01-Intro/01-overview.md | 4 +- docs/docs/11-Procedures/01-overview.md | 55 ++++++++++++++++---------- 2 files changed, 36 insertions(+), 23 deletions(-) diff --git a/docs/docs/01-Intro/01-overview.md b/docs/docs/01-Intro/01-overview.md index eed4c5c1d70..719482ed6b0 100644 --- a/docs/docs/01-Intro/01-overview.md +++ b/docs/docs/01-Intro/01-overview.md @@ -356,11 +356,11 @@ void Main() { if (res.IsSuccess) { - Log.Debug("Procedure `make_request` returned {Value}", res.Value); + Log.Debug($"Procedure `make_request` returned {res.Value!}"); } else { - throw new Exception(res.Error, "Procedure `make_request` failed!"); + throw new Exception($"Procedure `make_request` failed: {res.Error!}"); } }); } diff --git a/docs/docs/11-Procedures/01-overview.md b/docs/docs/11-Procedures/01-overview.md index dd63f34b578..929bdd81207 100644 --- a/docs/docs/11-Procedures/01-overview.md +++ b/docs/docs/11-Procedures/01-overview.md @@ -1,21 +1,25 @@ --- -Title: Procedures +Title: Overview slug: /procedures --- import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; +# Procedures - Overview + A **procedure** is a function exported by a [database](/#database), similar to a [reducer](/#reducer). -Connected [clients](/#client-side-sdks) can call procedures. +Connected [clients](/#client-side-sdks** can call procedures. Procedures can perform additional operations not possible in reducers, including making HTTP requests to external services. However, procedures don't automatically run in database transactions, and must manually open and commit a transaction in order to read from or modify the database state. For this reason, prefer defining reducers rather than procedures unless you need to use one of the special procedure operators. -Procedures are currently in beta, and their API may change in upcoming SpacetimeDB releases. +:::warning +***Procedures are currently in beta, and their API may change in upcoming SpacetimeDB releases.*** +::: -# Defining Procedures +## Defining Procedures @@ -24,7 +28,7 @@ Because procedures are unstable, Rust modules that define them must opt in to th ```toml [dependencies] -spacetimedb = { version = "1.x", features = ["unstable"] } +spacetimedb = { version = "1.*", features = ["unstable"] } ``` Define a procedure by annotating a function with `#[spacetimedb::procedure]`. @@ -61,7 +65,7 @@ TODO(noa) -## Accessing the database +### Accessing the database @@ -115,7 +119,7 @@ TODO(noa) -### Fallible database operations +#### Fallible database operations @@ -148,7 +152,7 @@ TODO(noa) -### Reading values out of the database +#### Reading values out of the database @@ -199,11 +203,14 @@ Procedures can make HTTP requests to external services using methods contained i #[spacetimedb::procedure] fn get_request(ctx: &mut ProcedureContext) { match ctx.http.get("https://example.invalid") { - Ok(response) => log::info!( - "Got response with status {} and body {}", - response.status(), - response.body().into_string_lossy(), - ), + Ok(response) => { + let (response, body) = response.into_parts(); + log::info!( + "Got response with status {} and body {}", + response.status(), + body.into_string_lossy(), + ) + }, Err(error) => log::error!("Request failed: {error:?}"), } } @@ -223,11 +230,14 @@ fn post_request(ctx: &mut spacetimedb::ProcedureContext) { .body("This is the body of the HTTP request") .expect("Building `Request` object failed"); match ctx.http.send(request) { - Ok(response) => log::info!( - "Got response with status {} and body {}", - response.status(), - response.body().into_string_lossy(), - ), + Ok(response) => { + let (response, body) = response.into_parts(); + log::info!( + "Got response with status {} and body {}", + response.status(), + body.into_string_lossy(), + ) + } Err(error) => log::error!("Request failed: {error:?}"), } } @@ -244,7 +254,10 @@ fn get_request_with_short_timeout(ctx: &mut spacetimedb::ProcedureContext) { let request = spacetimedb::http::Request::builder() .uri("https://example.invalid") .method("GET") - .extension(spacetimedb::http::Timeout(std::time::Duration::from_millis(10))) + // Set a timeout of 10 ms. + .extension(spacetimedb::http::Timeout(std::time::Duration::from_millis(10).into())) + // Empty body for a `GET` request. + .body(()) .expect("Building `Request` object failed"); ctx.http.send(request).expect("HTTP request failed"); } @@ -260,7 +273,7 @@ TODO(noa) -# Calling procedures +## Calling procedures @@ -301,7 +314,7 @@ Ctx->Procedures->InsertAValue(12, "Foo"); -## Observing return values +### Observing return values From 614a7097f93675ae1df4ea89839de87571587ab9 Mon Sep 17 00:00:00 2001 From: Phoebe Goldman Date: Wed, 26 Nov 2025 12:45:05 -0500 Subject: [PATCH 5/9] `Parts::status` is a field, not a method --- docs/docs/11-Procedures/01-overview.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/11-Procedures/01-overview.md b/docs/docs/11-Procedures/01-overview.md index 929bdd81207..9e3ef85c553 100644 --- a/docs/docs/11-Procedures/01-overview.md +++ b/docs/docs/11-Procedures/01-overview.md @@ -207,7 +207,7 @@ fn get_request(ctx: &mut ProcedureContext) { let (response, body) = response.into_parts(); log::info!( "Got response with status {} and body {}", - response.status(), + response.status, body.into_string_lossy(), ) }, @@ -234,7 +234,7 @@ fn post_request(ctx: &mut spacetimedb::ProcedureContext) { let (response, body) = response.into_parts(); log::info!( "Got response with status {} and body {}", - response.status(), + response.status, body.into_string_lossy(), ) } From 8011f1b3ea5d610be42f1784ea365871d8ecf495 Mon Sep 17 00:00:00 2001 From: Phoebe Goldman Date: Wed, 26 Nov 2025 12:57:29 -0500 Subject: [PATCH 6/9] UI tests with trybuild --- crates/bindings/tests/ui/views.stderr | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/crates/bindings/tests/ui/views.stderr b/crates/bindings/tests/ui/views.stderr index 9f46b8ff2e8..3796574ce28 100644 --- a/crates/bindings/tests/ui/views.stderr +++ b/crates/bindings/tests/ui/views.stderr @@ -408,3 +408,29 @@ error[E0277]: the trait bound `NotSpacetimeType: SpacetimeType` is not satisfied ColId and $N others = note: required for `Option` to implement `SpacetimeType` + +error[E0631]: type mismatch in function arguments + --> tests/ui/views.rs:142:56 + | +142 | #[spacetimedb::table(name = scheduled_table, scheduled(scheduled_table_view))] + | -------------------------------------------------------^^^^^^^^^^^^^^^^^^^^--- + | | | + | | expected due to this + | required by a bound introduced by this call +... +154 | fn scheduled_table_view(_: &ViewContext, _args: ScheduledTable) -> Vec { + | ------------------------------------------------------------------------------ found signature defined here + | + = note: expected function signature `for<'a> fn(&'a ReducerContext, ScheduledTable) -> _` + found function signature `fn(&ViewContext, ScheduledTable) -> _` + = note: required for `for<'a> fn(&'a ViewContext, ScheduledTable) -> Vec {scheduled_table_view}` to implement `Reducer<'_, (ScheduledTable,)>` + = note: required for `for<'a> fn(&'a ViewContext, ScheduledTable) -> Vec {scheduled_table_view}` to implement `ExportFunctionForScheduledTable<'_, ScheduledTable, {type error}>` +note: required by a bound in `scheduled_typecheck` + --> src/rt.rs + | + | pub const fn scheduled_typecheck<'de, Row, FnKind>(_x: impl ExportFunctionForScheduledTable<'de, Row, FnKind>) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `scheduled_typecheck` +help: consider wrapping the function in a closure + | +142 | #[spacetimedb::table(name = scheduled_table, scheduled(|arg0: &ReducerContext, arg1: ScheduledTable| scheduled_table_view(/* &ViewContext */, arg1)))] + | +++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++ From b95d1be3927ad685444041388d23114892e6dc1b Mon Sep 17 00:00:00 2001 From: Noa Date: Wed, 26 Nov 2025 12:15:05 -0600 Subject: [PATCH 7/9] Fill out typescript procedure docs --- docs/docs/11-Procedures/01-overview.md | 168 ++++++++++++++++++++++++- 1 file changed, 162 insertions(+), 6 deletions(-) diff --git a/docs/docs/11-Procedures/01-overview.md b/docs/docs/11-Procedures/01-overview.md index 9e3ef85c553..11f0558dc78 100644 --- a/docs/docs/11-Procedures/01-overview.md +++ b/docs/docs/11-Procedures/01-overview.md @@ -60,7 +60,26 @@ Support for procedures in C# modules is coming soon! -TODO(noa) +Define a procedure with `spacetimedb.procedure`: + +```typescript +spacetimedb.procedure( + "add_two_numbers", + { lhs: t.u32(), rhs: t.u32() }, + t.u64(), + (ctx, { lhs, rhs }) => BigInt(lhs) + BigInt(rhs), +); +``` + +The `spacetimedb.procedure` function takes: +* the procedure name, +* (optional) an object representing its parameter types, +* its return type, +* and the procedure function itself. + +The function will receive a `ProcedureContext` and an object of its arguments, and it must return +a value corresponding to its return type. This return value will be sent to the caller, but will +not be broadcast to any other clients. @@ -114,7 +133,49 @@ Avoid capturing mutable state within functions passed to `with_tx`. -TODO(noa) +Unlike reducers, procedures don't automatically run in database transactions. +This means there's no `ctx.db` field to access the database. +Instead, procedure code must manage transactions explicitly with `ProcedureCtx.withTx`. + +```typescript +const MyTable = table( + { name: "my_table" }, + { + a: t.u32(), + b: t.string(), + }, +) + +const spacetimedb = schema(MyTable); + +#[spacetimedb::procedure] +spacetimedb.procedure("insert_a_value", { a: t.u32(), b: t.u32() }, t.unit(), (ctx, { a, b }) => { + ctx.withTx(ctx => { + ctx.myTable.insert({ a, b }); + }); + return {}; +}) +``` + +`ProcedureCtx.withTx` takes a function of `(ctx: TransactionCtx) => T`. +Within that function, the `TransactionCtx` can be used to access the database +[in all the same ways as a `ReducerCtx`](/modules/typescript#reducercontext) +When the function returns, the transaction will be committed, +and its changes to the database state will become permanent and be broadcast to clients. +If the function throws an error, the transaction will be rolled back, and its changes will be discarded. + +:::warning +The function passed to `ProcedureCtx.withTx` may be invoked multiple times, +possibly seeing a different version of the database state each time. + +If invoked more than once with reference to the same database state, +it must perform the same operations and return the same result each time. + +If invoked more than once with reference to different database states, +values observed during prior runs must not influence the behavior of the function or the calling procedure. + +Avoid capturing mutable state within functions passed to `withTx`. +::: @@ -147,7 +208,18 @@ If that function returns `Err`, the transaction will be rolled back, and its cha -TODO(noa) +For fallible database operations, you can throw an error inside the transaction function: + +```typescript +spacetimedb.procedure("maybe_insert_a_value", { a: t.u32(), b: t.string() }, t.unit(), (ctx, { a, b }) => { + ctx.withTx(ctx => { + if (a < 10) { + throw new SenderError("a is less than 10!"); + } + ctx.myTable.insert({ a, b }); + }); +}) +``` @@ -185,7 +257,38 @@ fn find_highest_level_player(ctx: &mut ProcedureContext) { -TODO(noa) +Functions passed to +[`ProcedureCtx.withTx`](#accessing-the-database) +may return a value, and that value will be returned to the calling procedure. + +Transaction return values are never saved or broadcast to clients, and are used only by the calling procedure. + +```typescript +const Player = table( + { name: "player" }, + { + id: t.identity(), + level: t.u32(), + }, +); + +const spacetimedb = schema(Player); + +spacetimedb.procedure("find_highest_level_player", t.unit(), ctx => { + let highestLevelPlayer = ctx.withTx(ctx => + Iterator.from(ctx.db.player).reduce( + (a, b) => a == null || b.level > a.level ? b : a, + null + ) + ); + if (highestLevelPlayer != null) { + console.log("Congratulations to ", highestLevelPlayer.id); + } else { + console.warn("No players..."); + } + return {}; +}); +``` @@ -268,7 +371,60 @@ Procedures can't send requests at the same time as holding open a [transaction]( -TODO(noa) +Procedures can make HTTP requests to external services using methods contained in `ctx.http`. + +`ctx.http.fetch` is similar to the browser `fetch()` API, but is synchronous. + +It can perform simple `GET` requests: + +```typescript +#[spacetimedb::procedure] +spacetimedb.procedure("get_request", t.unit(), ctx => { + try { + const response = ctx.http.fetch("https://example.invalid"); + const body = response.text(); + console.log(`Got response with status ${response.status} and body ${body}`); + } catch (e) { + console.error("Request failed: ", e); + } + return {}; +}); +``` + +It can also accept an options object to specify a body, headers, HTTP method, and timeout: + +```typescript +spacetimedb.procedure("post_request", t.unit(), ctx => { + try { + const response = ctx.http.fetch("https://example.invalid/upload", { + method: "POST", + headers: { "Content-Type": "text/plain" }, + body: "This is the body of the HTTP request", + }); + const body = response.text(); + console.log(`Got response with status ${response.status} and body {body}`); + } catch (e) { + console.error("Request failed: ", e); + } + return {}; +}); + +spacetimedb.procedure("get_request_with_short_timeout", t.unit(), ctx => { + try { + const response = ctx.http.fetch("https://example.invalid", { + method: "GET", + timeout: TimeDuration.fromMillis(10), + }); + const body = response.text(); + console.log(`Got response with status ${response.status} and body {body}`); + } catch (e) { + console.error("Request failed: ", e); + } + return {}; +}); +``` + +Procedures can't send requests at the same time as holding open a [transaction](#accessing-the-database). @@ -358,7 +514,7 @@ When a client invokes a procedure, it gets a [`Promise`](https://developer.mozil ```typescript ctx.procedures.addTwoNumbers({ lhs: 1, rhs: 2 }).then( - sum => console.log(`1 + 2 = {sum}`) + sum => console.log(`1 + 2 = ${sum}`) ); ``` From 402678a64051a0c41b68293a2eb24f0fb5c305c5 Mon Sep 17 00:00:00 2001 From: Phoebe Goldman Date: Wed, 26 Nov 2025 14:15:08 -0500 Subject: [PATCH 8/9] Remove Unreal examples (for now) We'll need to get help from Jason to fill these back in later. --- docs/docs/11-Procedures/01-overview.md | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/docs/docs/11-Procedures/01-overview.md b/docs/docs/11-Procedures/01-overview.md index 11f0558dc78..f8acb7887e1 100644 --- a/docs/docs/11-Procedures/01-overview.md +++ b/docs/docs/11-Procedures/01-overview.md @@ -458,15 +458,6 @@ Clients can invoke procedures using methods on `ctx.procedures`: ctx.procedures.insertAValue({ a: 12, b: "Foo" }); ``` - - - -Clients can invoke procedures using methods on `Ctx->Procedures`: - -```cpp -Ctx->Procedures->InsertAValue(12, "Foo"); -``` - @@ -518,14 +509,5 @@ ctx.procedures.addTwoNumbers({ lhs: 1, rhs: 2 }).then( ); ``` - - - -A client can also invoke a procedure while registering a callback to run when it completes. -That callback will have access to the return value of the procedure, -or an error if the procedure fails. - -TODO - From 9ce7332d7fb271420c04e7eb0653709219452f39 Mon Sep 17 00:00:00 2001 From: Phoebe Goldman Date: Wed, 26 Nov 2025 16:00:37 -0500 Subject: [PATCH 9/9] fix a mistake when downcasing a dir --- docs/docs/14-HTTP API/01-authorization.md | 28 -- docs/docs/14-HTTP API/02-identity.md | 132 ----- docs/docs/14-HTTP API/03-database.md | 451 ------------------ .../01-authorization.md | 0 .../02-identity.md | 0 .../03-database.md | 0 .../_category_.json | 0 7 files changed, 611 deletions(-) delete mode 100644 docs/docs/14-HTTP API/01-authorization.md delete mode 100644 docs/docs/14-HTTP API/02-identity.md delete mode 100644 docs/docs/14-HTTP API/03-database.md rename docs/docs/{13-http-api => 14-http-api}/01-authorization.md (100%) rename docs/docs/{13-http-api => 14-http-api}/02-identity.md (100%) rename docs/docs/{13-http-api => 14-http-api}/03-database.md (100%) rename docs/docs/{13-http-api => 14-http-api}/_category_.json (100%) diff --git a/docs/docs/14-HTTP API/01-authorization.md b/docs/docs/14-HTTP API/01-authorization.md deleted file mode 100644 index 73552981182..00000000000 --- a/docs/docs/14-HTTP API/01-authorization.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -title: Authorization -slug: /http/authorization ---- - -# SpacetimeDB HTTP Authorization - -### Generating identities and tokens - -SpacetimeDB can derive an identity from the `sub` and `iss` claims of any [OpenID Connect](https://openid.net/developers/how-connect-works/) compliant [JSON Web Token](https://jwt.io/). - -Clients can request a new identity and token signed by the SpacetimeDB host via [the `POST /v1/identity` HTTP endpoint](/http/identity#post-v1identity). Such a token will not be portable to other SpacetimeDB clusters. - -Alternately, a new identity and token will be generated during an anonymous connection via the WebSocket API, and passed to the client as an `IdentityToken` message. - -### `Authorization` headers - -Many SpacetimeDB HTTP endpoints either require or optionally accept a token in the `Authorization` header. SpacetimeDB authorization headers are of the form `Authorization: Bearer ${token}`, where `token` is an [OpenID Connect](https://openid.net/developers/how-connect-works/) compliant [JSON Web Token](https://jwt.io/), such as the one returned from [the `POST /v1/identity` HTTP endpoint](/http/identity#post-v1identity). - -# Top level routes - -| Route | Description | -| ----------------------------- | ------------------------------------------------------ | -| [`GET /v1/ping`](#get-v1ping) | No-op. Used to determine whether a client can connect. | - -## `GET /v1/ping` - -Does nothing and returns no data. Clients can send requests to this endpoint to determine whether they are able to connect to SpacetimeDB. diff --git a/docs/docs/14-HTTP API/02-identity.md b/docs/docs/14-HTTP API/02-identity.md deleted file mode 100644 index 7a7e6fdf6b5..00000000000 --- a/docs/docs/14-HTTP API/02-identity.md +++ /dev/null @@ -1,132 +0,0 @@ ---- -slug: /http/identity ---- - -# `/v1/identity` - -The HTTP endpoints in `/v1/identity` allow clients to generate and manage Spacetime public identities and private tokens. - -## At a glance - -| Route | Description | -| -------------------------------------------------------------------------- | ------------------------------------------------------------------ | -| [`POST /v1/identity`](#post-v1identity) | Generate a new identity and token. | -| [`POST /v1/identity/websocket-token`](#post-v1identitywebsocket-token) | Generate a short-lived access token for use in untrusted contexts. | -| [`GET /v1/identity/public-key`](#get-v1identitypublic-key) | Get the public key used for verifying tokens. | -| [`GET /v1/identity/:identity/databases`](#get-v1identityidentitydatabases) | List databases owned by an identity. | -| [`GET /v1/identity/:identity/verify`](#get-v1identityidentityverify) | Verify an identity and token. | - -## `POST /v1/identity` - -Create a new identity. - -#### Returns - -Returns JSON in the form: - -```typescript -{ - "identity": string, - "token": string -} -``` - -## `POST /v1/identity/websocket-token` - -Generate a short-lived access token which can be used in untrusted contexts, e.g. embedded in URLs. - -#### Required Headers - -| Name | Value | -| --------------- | ----------------------------------------------------------------------------- | -| `Authorization` | A Spacetime token [encoded as Basic authorization](/http/authorization). | - -#### Returns - -Returns JSON in the form: - -```typescript -{ - "token": string -} -``` - -The `token` value is a short-lived [JSON Web Token](https://datatracker.ietf.org/doc/html/rfc7519). - -## `GET /v1/identity/public-key` - -Fetches the public key used by the database to verify tokens. - -#### Returns - -Returns a response of content-type `application/pem-certificate-chain`. - -## `POST /v1/identity/:identity/set-email` - -Associate an email with a Spacetime identity. - -#### Parameters - -| Name | Value | -| ----------- | ----------------------------------------- | -| `:identity` | The identity to associate with the email. | - -#### Query Parameters - -| Name | Value | -| ------- | ----------------- | -| `email` | An email address. | - -#### Required Headers - -| Name | Value | -| --------------- | ----------------------------------------------------------------------------- | -| `Authorization` | A Spacetime token [encoded as Basic authorization](/http/authorization). | - -## `GET /v1/identity/:identity/databases` - -List all databases owned by an identity. - -#### Parameters - -| Name | Value | -| ----------- | --------------------- | -| `:identity` | A Spacetime identity. | - -#### Returns - -Returns JSON in the form: - -```typescript -{ - "addresses": array -} -``` - -The `addresses` value is an array of zero or more strings, each of which is the address of a database owned by the identity passed as a parameter. - -## `GET /v1/identity/:identity/verify` - -Verify the validity of an identity/token pair. - -#### Parameters - -| Name | Value | -| ----------- | ----------------------- | -| `:identity` | The identity to verify. | - -#### Required Headers - -| Name | Value | -| --------------- | ----------------------------------------------------------------------------- | -| `Authorization` | A Spacetime token [encoded as Basic authorization](/http/authorization). | - -#### Returns - -Returns no data. - -If the token is valid and matches the identity, returns `204 No Content`. - -If the token is valid but does not match the identity, returns `400 Bad Request`. - -If the token is invalid, or no `Authorization` header is included in the request, returns `401 Unauthorized`. diff --git a/docs/docs/14-HTTP API/03-database.md b/docs/docs/14-HTTP API/03-database.md deleted file mode 100644 index 7055304f0cb..00000000000 --- a/docs/docs/14-HTTP API/03-database.md +++ /dev/null @@ -1,451 +0,0 @@ ---- -slug: /http/database ---- - -# `/v1/database` - -The HTTP endpoints in `/v1/database` allow clients to interact with Spacetime databases in a variety of ways, including retrieving information, creating and deleting databases, invoking reducers and evaluating SQL queries. - -## At a glance - -| Route | Description | -| -------------------------------------------------------------------------------------------------- | ------------------------------------------------- | -| [`POST /v1/database`](#post-v1database) | Publish a new database given its module code. | -| [`POST /v1/database/:name_or_identity`](#post-v1databasename_or_identity) | Publish to a database given its module code. | -| [`GET /v1/database/:name_or_identity`](#get-v1databasename_or_identity) | Get a JSON description of a database. | -| [`DELETE /v1/database/:name_or_identity`](#post-v1databasename_or_identity) | Delete a database. | -| [`GET /v1/database/:name_or_identity/names`](#get-v1databasename_or_identitynames) | Get the names this database can be identified by. | -| [`POST /v1/database/:name_or_identity/names`](#post-v1databasename_or_identitynames) | Add a new name for this database. | -| [`PUT /v1/database/:name_or_identity/names`](#put-v1databasename_or_identitynames) | Set the list of names for this database. | -| [`GET /v1/database/:name_or_identity/identity`](#get-v1databasename_or_identityidentity) | Get the identity of a database. | -| [`GET /v1/database/:name_or_identity/subscribe`](#get-v1databasename_or_identitysubscribe) | Begin a WebSocket connection. | -| [`POST /v1/database/:name_or_identity/call/:reducer`](#post-v1databasename_or_identitycallreducer) | Invoke a reducer in a database. | -| [`GET /v1/database/:name_or_identity/schema`](#get-v1databasename_or_identityschema) | Get the schema for a database. | -| [`GET /v1/database/:name_or_identity/logs`](#get-v1databasename_or_identitylogs) | Retrieve logs from a database. | -| [`POST /v1/database/:name_or_identity/sql`](#post-v1databasename_or_identitysql) | Run a SQL query against a database. | - -## `POST /v1/database` - -Publish a new database with no name. - -Accessible through the CLI as `spacetime publish`. - -#### Required Headers - -| Name | Value | -| --------------- | ----------------------------------------------------------------------------------- | -| `Authorization` | A Spacetime token [as Bearer auth](/http/authorization#authorization-headers). | - -#### Data - -A WebAssembly module in the [binary format](https://webassembly.github.io/spec/core/binary/index.html). - -#### Returns - -If the database was successfully published, returns JSON in the form: - -```typescript -{ "Success": { - "database_identity": string, - "op": "created" | "updated" -} } -``` - -## `POST /v1/database/:name_or_identity` - -Publish to a database with the specified name or identity. If the name doesn't exist, creates a new database. - -Accessible through the CLI as `spacetime publish`. - -#### Query Parameters - -| Name | Value | -| ------- | --------------------------------------------------------------------------------- | -| `clear` | A boolean; whether to clear any existing data when updating an existing database. | - -#### Required Headers - -| Name | Value | -| --------------- | ----------------------------------------------------------------------------------- | -| `Authorization` | A Spacetime token [as Bearer auth](/http/authorization#authorization-headers). | - -#### Data - -A WebAssembly module in the [binary format](https://webassembly.github.io/spec/core/binary/index.html). - -#### Returns - -If the database was successfully published, returns JSON in the form: - -```typescript -{ "Success": { - "domain": null | string, - "database_identity": string, - "op": "created" | "updated" -} } -``` - -If a database with the given name exists, but the identity provided in the `Authorization` header does not have permission to edit it, returns `401 UNAUTHORIZED` along with JSON in the form: - -```typescript -{ "PermissionDenied": { - "name": string -} } -``` - -## `GET /v1/database/:name_or_identity` - -Get a database's identity, owner identity, host type, number of replicas and a hash of its WASM module. - -#### Returns - -Returns JSON in the form: - -```typescript -{ - "database_identity": string, - "owner_identity": string, - "host_type": "wasm", - "initial_program": string -} -``` - -| Field | Type | Meaning | -| --------------------- | ------ | ---------------------------------------------------------------- | -| `"database_identity"` | String | The Spacetime identity of the database. | -| `"owner_identity"` | String | The Spacetime identity of the database's owner. | -| `"host_type"` | String | The module host type; currently always `"wasm"`. | -| `"initial_program"` | String | Hash of the WASM module with which the database was initialized. | - -## `DELETE /v1/database/:name_or_identity` - -Delete a database. - -Accessible through the CLI as `spacetime delete `. - -#### Required Headers - -| Name | Value | -| --------------- | ----------------------------------------------------------------------------------- | -| `Authorization` | A Spacetime token [as Bearer auth](/http/authorization#authorization-headers). | - -## `GET /v1/database/:name_or_identity/names` - -Get the names this database can be identified by. - -#### Returns - -Returns JSON in the form: - -```typescript -{ "names": array } -``` - -where `` is a JSON array of strings, each of which is a name which refers to the database. - -## `POST /v1/database/:name_or_identity/names` - -Add a new name for this database. - -#### Required Headers - -| Name | Value | -| --------------- | ----------------------------------------------------------------------------------- | -| `Authorization` | A Spacetime token [as Bearer auth](/http/authorization#authorization-headers). | - -#### Data - -Takes as the request body a string containing the new name of the database. - -#### Returns - -If the name was successfully set, returns JSON in the form: - -```typescript -{ "Success": { - "domain": string, - "database_result": string -} } -``` - -If the new name already exists but the identity provided in the `Authorization` header does not have permission to edit it, returns JSON in the form: - -```typescript -{ "PermissionDenied": { - "domain": string -} } -``` - -## `PUT /v1/database/:name_or_identity/names` - -Set the list of names for this database. - -#### Required Headers - -| Name | Value | -| --------------- | ----------------------------------------------------------------------------------- | -| `Authorization` | A Spacetime token [as Bearer auth](/http/authorization#authorization-headers). | - -#### Data - -Takes as the request body a list of names, as a JSON array of strings. - -#### Returns - -If the name was successfully set, returns JSON in the form: - -```typescript -{ "Success": null } -``` - -If any of the new names already exist but the identity provided in the `Authorization` header does not have permission to edit it, returns `401 UNAUTHORIZED` along with JSON in the form: - -```typescript -{ "PermissionDenied": null } -``` - -## `GET /v1/database/:name_or_identity/identity` - -Get the identity of a database. - -#### Returns - -Returns a hex string of the specified database's identity. - -## `GET /v1/database/:name_or_identity/subscribe` - -Begin a WebSocket connection with a database. - -#### Required Headers - -For more information about WebSocket headers, see [RFC 6455](https://datatracker.ietf.org/doc/html/rfc6455). - -| Name | Value | -| ------------------------ | --------------------------------------------------------------------- | -| `Sec-WebSocket-Protocol` | `v1.bsatn.spacetimedb` or `v1.json.spacetimedb` | -| `Connection` | `Updgrade` | -| `Upgrade` | `websocket` | -| `Sec-WebSocket-Version` | `13` | -| `Sec-WebSocket-Key` | A 16-byte value, generated randomly by the client, encoded as Base64. | - -The SpacetimeDB binary WebSocket protocol, `v1.bsatn.spacetimedb`, encodes messages as well as reducer and row data using [BSATN](/bsatn). -Its messages are defined [here](https://github.com/clockworklabs/SpacetimeDB/blob/master/crates/client-api-messages/src/websocket.rs). - -The SpacetimeDB text WebSocket protocol, `v1.json.spacetimedb`, encodes messages according to the [SATS-JSON format](/sats-json). - -#### Optional Headers - -| Name | Value | -| --------------- | ----------------------------------------------------------------------------------- | -| `Authorization` | A Spacetime token [as Bearer auth](/http/authorization#authorization-headers). | - -## `POST /v1/database/:name_or_identity/call/:reducer` - -Invoke a reducer in a database. - -#### Path parameters - -| Name | Value | -| ---------- | ------------------------ | -| `:reducer` | The name of the reducer. | - -#### Required Headers - -| Name | Value | -| --------------- | ----------------------------------------------------------------------------------- | -| `Authorization` | A Spacetime token [as Bearer auth](/http/authorization#authorization-headers). | - -#### Data - -A JSON array of arguments to the reducer. - -## `GET /v1/database/:name_or_identity/schema` - -Get a schema for a database. - -Accessible through the CLI as `spacetime describe `. - -#### Query Parameters - -| Name | Value | -| --------- | ------------------------------------------------ | -| `version` | The version of `RawModuleDef` to return, e.g. 9. | - -#### Returns - -Returns a `RawModuleDef` in JSON form. - -
-Example response from `/schema?version=9` for the default module generated by `spacetime init` - -```json -{ - "typespace": { - "types": [ - { - "Product": { - "elements": [ - { - "name": { - "some": "name" - }, - "algebraic_type": { - "String": [] - } - } - ] - } - } - ] - }, - "tables": [ - { - "name": "person", - "product_type_ref": 0, - "primary_key": [], - "indexes": [], - "constraints": [], - "sequences": [], - "schedule": { - "none": [] - }, - "table_type": { - "User": [] - }, - "table_access": { - "Private": [] - } - } - ], - "reducers": [ - { - "name": "add", - "params": { - "elements": [ - { - "name": { - "some": "name" - }, - "algebraic_type": { - "String": [] - } - } - ] - }, - "lifecycle": { - "none": [] - } - }, - { - "name": "identity_connected", - "params": { - "elements": [] - }, - "lifecycle": { - "some": { - "OnConnect": [] - } - } - }, - { - "name": "identity_disconnected", - "params": { - "elements": [] - }, - "lifecycle": { - "some": { - "OnDisconnect": [] - } - } - }, - { - "name": "init", - "params": { - "elements": [] - }, - "lifecycle": { - "some": { - "Init": [] - } - } - }, - { - "name": "say_hello", - "params": { - "elements": [] - }, - "lifecycle": { - "none": [] - } - } - ], - "types": [ - { - "name": { - "scope": [], - "name": "Person" - }, - "ty": 0, - "custom_ordering": true - } - ], - "misc_exports": [], - "row_level_security": [] -} -``` - -
- -## `GET /v1/database/:name_or_identity/logs` - -Retrieve logs from a database. - -Accessible through the CLI as `spacetime logs `. - -#### Query Parameters - -| Name | Value | -| ----------- | --------------------------------------------------------------- | -| `num_lines` | Number of most-recent log lines to retrieve. | -| `follow` | A boolean; whether to continue receiving new logs via a stream. | - -#### Required Headers - -| Name | Value | -| --------------- | ----------------------------------------------------------------------------------- | -| `Authorization` | A Spacetime token [as Bearer auth](/http/authorization#authorization-headers). | - -#### Returns - -Text, or streaming text if `follow` is supplied, containing log lines. - -## `POST /v1/database/:name_or_identity/sql` - -Run a SQL query against a database. - -Accessible through the CLI as `spacetime sql `. - -#### Required Headers - -| Name | Value | -| --------------- | ----------------------------------------------------------------------------------- | -| `Authorization` | A Spacetime token [as Bearer auth](/http/authorization#authorization-headers). | - -#### Data - -SQL queries, separated by `;`. - -#### Returns - -Returns a JSON array of statement results, each of which takes the form: - -```typescript -{ - "schema": ProductType, - "rows": array -} -``` - -The `schema` will be a [JSON-encoded `ProductType`](/sats-json) describing the type of the returned rows. - -The `rows` will be an array of [JSON-encoded `ProductValue`s](/sats-json), each of which conforms to the `schema`. diff --git a/docs/docs/13-http-api/01-authorization.md b/docs/docs/14-http-api/01-authorization.md similarity index 100% rename from docs/docs/13-http-api/01-authorization.md rename to docs/docs/14-http-api/01-authorization.md diff --git a/docs/docs/13-http-api/02-identity.md b/docs/docs/14-http-api/02-identity.md similarity index 100% rename from docs/docs/13-http-api/02-identity.md rename to docs/docs/14-http-api/02-identity.md diff --git a/docs/docs/13-http-api/03-database.md b/docs/docs/14-http-api/03-database.md similarity index 100% rename from docs/docs/13-http-api/03-database.md rename to docs/docs/14-http-api/03-database.md diff --git a/docs/docs/13-http-api/_category_.json b/docs/docs/14-http-api/_category_.json similarity index 100% rename from docs/docs/13-http-api/_category_.json rename to docs/docs/14-http-api/_category_.json