Integrating SpacetimeDB with External Services: A Complete Guide to HTTP Egress
SpacetimeDB procedures can call external HTTP APIs using the built-in HttpClient available on ProcedureContext, enabling seamless integration with webhooks, REST services, and third-party data sources while maintaining the platform's sandboxed WebAssembly security model.
Integrating SpacetimeDB with external services allows your database logic to fetch configuration, trigger notifications, or enrich data in real-time. Despite running inside a sandboxed WebAssembly module, SpacetimeDB provides a built-in HTTP client exposed through the ProcedureContext struct, enabling outbound requests without requiring external dependencies like reqwest.
How SpacetimeDB HTTP Egress Works
The HTTP client architecture bridges the WebAssembly sandbox and the host runtime through a tightly controlled interface defined in crates/bindings/src/lib.rs and crates/bindings/src/http.rs.
The ProcedureContext Interface
Every SpacetimeDB procedure receives a mutable reference to ProcedureContext, which contains request-specific metadata and the http: HttpClient field. According to the source code in crates/bindings/src/lib.rs (lines 80-82), this context provides the gateway for all outbound communication:
pub struct ProcedureContext {
pub http: HttpClient,
// ... identity, timestamp, etc.
}
HttpClient Implementation and BSATN Serialization
The HttpClient struct, defined in crates/bindings/src/http.rs (lines 27-34), acts as a thin wrapper that serializes standard http::Request objects into SpacetimeDB's wire format (BSATN). The client delegates actual network execution to the host via spacetimedb_bindings_sys::procedure::http_request.
The conversion process in crates/bindings/src/http.rs (lines 39-71) preserves HTTP method, URI, headers, and an optional Timeout extension, ensuring compatibility with standard HTTP semantics while maintaining sandbox constraints.
Request Timeouts and Error Handling
SpacetimeDB enforces a hard limit of 500 ms for all outbound HTTP requests to prevent blocking the database runtime. Procedures can specify a lower timeout by adding a http::Timeout extension to the request builder, as implemented in crates/bindings/src/http.rs (lines 31-35).
The error model distinguishes between transport failures and HTTP error codes. According to crates/bindings/src/http.rs (lines 37-40), HttpClient::send returns Result<Response, Error> where Error indicates network-level failures (DNS resolution, connection refused), while HTTP status codes like 404 or 500 are returned as successful Response objects that the procedure must handle explicitly.
Practical Examples: Calling External APIs from SpacetimeDB
The following examples demonstrate how to integrate SpacetimeDB with external services using the ProcedureContext HTTP client.
Simple GET Request to Fetch External Data
This example retrieves repository information from the GitHub API. The ctx.http.get shortcut constructs a basic GET request without custom headers:
use spacetimedb::{procedure, ProcedureContext};
#[procedure]
fn fetch_github_api(ctx: &mut ProcedureContext) {
match ctx.http.get("https://api.github.com/repos/clockworklabs/SpacetimeDB") {
Ok(response) => {
let (parts, body) = response.into_parts();
log::info!("GitHub returned {} bytes, status {}", body.into_bytes().len(), parts.status);
}
Err(err) => {
log::error!("Failed to call GitHub API: {err}");
}
}
}
The response body implements into_bytes() and into_string_lossy() methods for content inspection.
POST Request with JSON Payload and Custom Timeout
When integrating with webhooks or REST APIs that require POST methods, use the http::Request::builder() pattern to set headers, method, and timeout extensions. This example sends a JSON payload with a 200 ms timeout, overriding the default 500 ms limit:
use spacetimedb::{procedure, ProcedureContext, http::Timeout};
use std::time::Duration;
use http::header::CONTENT_TYPE;
#[procedure]
fn post_to_webhook(ctx: &mut ProcedureContext) {
let json = r#"{"event":"user_signup","user_id":42}"#;
let request = http::Request::builder()
.method(http::Method::POST)
.uri("https://example.com/webhook")
.header(CONTENT_TYPE, "application/json")
.extension(Timeout::from(Duration::from_millis(200)))
.body(json.to_string())
.expect("invalid request");
match ctx.http.send(request) {
Ok(resp) => {
let (parts, body) = resp.into_parts();
log::info!("Webhook responded {} – {}", parts.status, body.into_string_lossy());
}
Err(e) => log::error!("Webhook POST failed: {e}"),
}
}
The Timeout extension accepts any Duration up to 500 ms. Requests exceeding this limit trigger a host-level cancellation.
Handling HTTP Error Responses Gracefully
HTTP status codes such as 404 or 500 are returned as Ok(Response) variants, not as Err values. Procedures must explicitly check status().is_success() to distinguish between successful and failed application-level responses:
use spacetimedb::{procedure, ProcedureContext};
#[procedure]
fn fetch_with_status_check(ctx: &mut ProcedureContext) {
let url = "https://httpbin.org/status/404";
match ctx.http.get(url) {
Ok(resp) => {
let status = resp.status();
if status.is_success() {
let body = resp.into_body().into_string_lossy();
log::info!("Success: {body}");
} else {
log::warn!("Request to {url} returned HTTP {status}");
}
}
Err(e) => log::error!("Network error: {e}"),
}
}
Transport errors (DNS failures, connection timeouts, TLS errors) return Err(Error), while HTTP semantic errors return Ok(Response) with the appropriate status code.
Key Source Files for HTTP Integration
The following files in the clockworklabs/SpacetimeDB repository define the HTTP egress functionality:
| File | Purpose |
|---|---|
crates/bindings/src/lib.rs |
Defines ProcedureContext with the http: HttpClient field (lines 80-82). |
crates/bindings/src/http.rs |
Implements HttpClient, request conversion, BSATN serialization, timeout handling, and response reconstruction. |
crates/bindings/src/rt.rs |
Contains macro-generated glue that injects the http field into procedure contexts at runtime. |
crates/smoke-tests/tests/http_egress.rs |
Integration tests validating outbound HTTP functionality, including IP disallow lists. |
Summary
- SpacetimeDB procedures interact with external services via the
HttpClientavailable onProcedureContext. - BSATN serialization converts standard
http::Requestobjects into the wire format used by the host runtime. - 500 ms hard limit caps all outbound requests, with optional lower timeouts set via the
Timeoutextension. - Error handling distinguishes transport failures (
Err) from HTTP error codes (Ok(Response)). - Zero external dependencies are required; the client is built into the SpacetimeDB bindings.
Frequently Asked Questions
Can SpacetimeDB procedures accept incoming HTTP requests?
No. The HTTP client exposed on ProcedureContext is strictly for outbound egress. SpacetimeDB modules expose callable procedures to clients through the SpacetimeDB SDK and WebSocket connections, not through direct inbound HTTP endpoints. Incoming requests must use the standard SpacetimeDB client libraries.
What is the maximum timeout for HTTP requests in SpacetimeDB?
The host enforces a hard ceiling of 500 milliseconds for all outbound HTTP requests to prevent blocking the database runtime. Procedures can specify a shorter timeout by attaching a Timeout extension to the request builder, but cannot exceed the 500 ms limit.
How does SpacetimeDB serialize HTTP requests for WebAssembly?
SpacetimeDB uses BSATN (Binary SpacetimeDB Algebraic Notation) to serialize http::Request objects before passing them to the host. The HttpClient in crates/bindings/src/http.rs handles this conversion, transforming standard HTTP parts (method, URI, headers) into the wire format expected by spacetimedb_bindings_sys::procedure::http_request.
Can I use external HTTP client libraries like reqwest in SpacetimeDB?
No. SpacetimeDB procedures run inside a sandboxed WebAssembly module that cannot perform unsandboxed network I/O. You must use the built-in HttpClient provided on ProcedureContext, which delegates network operations to the trusted host runtime. This design ensures deterministic execution and security while still enabling external service integration.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →