Documentation
Quick Start Guide
Get Stackhouse running in 5 minutes
Quick Start Guide
🚀 Get Stackhouse Running in 5 Minutes
From zero to Stackhouse in just 5 minutes!
📋 Prerequisites
Stackhouse is a Rust/Axum service backed by PostgreSQL, with Qdrant required only for vector-search features. Before you begin, ensure you have:
Mandatory:
- ☑ Rust toolchain (1.70+) — install from rustup.rs
- ☑ Git — install from git-scm.com
- ☑ A running PostgreSQL instance — Stackhouse stores all data here (see
docker-compose.yml)
Optional:
- ☐ Docker + docker-compose (easiest way to get Postgres and Qdrant running together)
- ☐ Qdrant (only needed for
/v1/vectors/*endpoints) - ☐ curl or Postman (for API testing)
A JWT secret (STACKHOUSE_JWT_SECRET / --jwt-secret) is required to run the server — there is no default.
🎯 Installation Options
The repo ships stackhouse/docker-compose.yml, which builds the Stackhouse image locally and wires it to Postgres and Qdrant containers automatically:
cd stackhouse
docker compose up --buildThis starts Postgres on 5432, Qdrant on 6333/6334, and Stackhouse on 8080 (STACKHOUSE_PORT=8080 in that file — note this differs from the CLI's own default of 3000, see below). Check docker-compose.yml for the required env vars (STACKHOUSE_URL, STACKHOUSE_JWT_SECRET, STACKHOUSE_DATA_ENCRYPTION_KEY, etc.) before adapting it for anything beyond local testing — the checked-in file uses placeholder secrets.
🏃 Quick Start
Step 1: Start the Server
The CLI is subcommand-based (stackhouse <command>) — there's no bare stackhouse that starts a server. To start the API server, use stackhouse serve:
# Start with default settings (port 3000, requires STACKHOUSE_URL and
# STACKHOUSE_JWT_SECRET to be set, e.g. via env vars or a .env file)
stackhouse serve
# Or customize
stackhouse serve \
--port 8080 \
--host 0.0.0.0 \
--jwt-secret some-long-dev-secretReal flags on stackhouse serve (stackhouse/src/cli/mod.rs, ServeArgs): --port (default 3000), --host (default 0.0.0.0), --memory (use an isolated test schema), --jwt-secret, --storage-path. There is no --db or --log-level flag — use STACKHOUSE_URL for the Postgres connection string and the standard RUST_LOG env var for log verbosity.
On successful start, the server logs (approximately):
✨ Stackhouse initialized successfully with PostgreSQL
⚡ Initializing Realtime Engine...Available once running:
- API:
http://localhost:3000(or your--port) - Explorer dashboard:
http://localhost:3000/explore - WebSocket:
ws://localhost:3000/v1/realtime
Step 2: Verify Installation
curl http://localhost:3000/healthResponse:
{
"status": "healthy",
"database": "connected"
}Step 3: Open the Dashboard
Open browser: http://localhost:3000/explore
You'll see the Stackhouse Explorer:
Tables
Query
Stats
💡 Tip: Use the API or dashboard to insert data.
🎨 Your First Queries
Example 1: Auto-Schema Creation
# Insert your first document
curl -X POST http://localhost:3000/v1/push/users \
-H "Content-Type: application/json" \
-d '{
"name": "Alice Johnson",
"email": "alice@example.com",
"age": 28
}'What happens behind the scenes:
- Stackhouse receives the JSON.
- Analyzes the structure:
name: String → TEXT,email: String → TEXT,age: Number → BIGINT. - Creates the table (id/timestamps), then adds columns:
CREATE TABLE IF NOT EXISTS users ( id BIGSERIAL PRIMARY KEY, created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP ); ALTER TABLE users ADD COLUMN IF NOT EXISTS name TEXT; ALTER TABLE users ADD COLUMN IF NOT EXISTS email TEXT; ALTER TABLE users ADD COLUMN IF NOT EXISTS age BIGINT; - Inserts the data.
- Returns success.
Response:
{
"success": true,
"data": {
"id": 1,
"name": "Alice Johnson",
"email": "alice@example.com",
"age": 28,
"created_at": "2025-01-03T12:00:00Z"
}
}Example 2: Schema Evolution
# Insert data with new fields
curl -X POST http://localhost:3000/v1/push/users \
-H "Content-Type: application/json" \
-d '{
"name": "Bob Smith",
"age": 35,
"city": "San Francisco",
"skills": ["Rust", "Python", "JavaScript"]
}'Detected new fields: city: String → TEXT (added), skills: Array → JSONB (added).
Automatic migration:
ALTER TABLE users ADD COLUMN IF NOT EXISTS city TEXT;
ALTER TABLE users ADD COLUMN IF NOT EXISTS skills JSONB;⚡ Zero downtime required.
Example 3: Query Data
# Get all users
curl http://localhost:3000/v1/query/users
# Get specific user
curl http://localhost:3000/v1/query/users/1Response:
{
"success": true,
"data": [
{
"id": 1,
"name": "Alice Johnson",
"email": "alice@example.com",
"age": 28,
"created_at": "2025-01-03T12:00:00Z"
},
{
"id": 2,
"name": "Bob Smith",
"age": 35,
"city": "San Francisco",
"skills": ["Rust", "Python", "JavaScript"],
"created_at": "2025-01-03T12:01:00Z"
}
]
}🧪 Advanced Quick Start
Vector Search Setup
Requires a reachable Qdrant instance (QDRANT_URL, see docker-compose.yml) — Stackhouse proxies these requests to Qdrant, it doesn't index vectors itself.
# 1. Insert (upsert) a vector
curl -X POST http://localhost:3000/v1/vectors/documents/upsert \
-H "Content-Type: application/json" \
-d '{
"id": "doc1",
"embedding": [0.1, 0.2, 0.3, 0.4, 0.5],
"data": {
"title": "Introduction to Stackhouse",
"category": "database"
}
}'
# 2. Insert more vectors
curl -X POST http://localhost:3000/v1/vectors/documents/upsert \
-H "Content-Type: application/json" \
-d '{
"id": "doc2",
"embedding": [0.15, 0.25, 0.35, 0.45, 0.55],
"data": {
"title": "Advanced Stackhouse Features",
"category": "database"
}
}'
# 3. Search for similar vectors
curl -X POST http://localhost:3000/v1/vectors/documents/search \
-H "Content-Type: application/json" \
-d '{
"vector": [0.12, 0.22, 0.32, 0.42, 0.52],
"top_k": 5
}'Response:
{
"success": true,
"count": 2,
"collection": "documents",
"metric": "cosine",
"data": [
{
"id": "doc2",
"similarity": 0.98,
"data": {
"title": "Advanced Stackhouse Features",
"category": "database"
}
},
{
"id": "doc1",
"similarity": 0.95,
"data": {
"title": "Introduction to Stackhouse",
"category": "database"
}
}
]
}WebSocket Realtime Connection
// Connect to Stackhouse WebSocket
const ws = new WebSocket('ws://localhost:3000/v1/realtime');
// Connection opened
ws.addEventListener('open', () => {
console.log('✅ Connected to Stackhouse');
// Subscribe to a table (real protocol: lowercase "subscribe", "table", "event")
ws.send(JSON.stringify({
type: 'subscribe',
table: 'users',
event: '*'
}));
});
// Listen for messages
ws.addEventListener('message', (event) => {
const msg = JSON.parse(event.data);
// Real event types are "INSERT" / "UPDATE" / "DELETE"
if (msg.type === 'INSERT') {
console.log('📨 New row:', msg.table, msg.record);
// Example output:
// 📨 New row: users {
// id: 3,
// name: "Charlie",
// ...
// }
}
});
// Now try inserting data from another terminal:
// curl -X POST http://localhost:3000/v1/push/users \
// -H "Content-Type: application/json" \
// -d '{"name": "Charlie", "age": 30}'
//
// You'll see it appear instantly in the WebSocket!📊 Quick Reference Commands
Verified against stackhouse/src/api/routes.rs and stackhouse/src/storage/vectors.rs.
Data Operations
# Insert data
POST /v1/push/:collection
POST /v1/push/:collection/batch
# Query data (equality filters via query params, limit/offset/order_dir)
GET /v1/query/:collection
GET /v1/query/:collection/:id
# Update data
POST /v1/update/:collection # bulk
# Delete data
POST /v1/delete/:collection # bulk
# Raw SQL (admin-gated, off by default)
POST /v1/sql/query
POST /v1/sql/executeSchema & Metadata
# List all tables
GET /v1/tables
# Get table stats / drop a table
GET /v1/tables/:collection
DELETE /v1/tables/:collectionThere is currently no REST endpoint for creating an index — see Indexing.
Vector Search (proxied to Qdrant)
POST /v1/vectors/:collection/upsert
POST /v1/vectors/:collection/batch
POST /v1/vectors/:collection/search
GET /v1/vectors/:collection/infoThere is no list-all-collections or delete-by-id vector endpoint.
JavaScript Functions — implemented, NOT currently reachable
# These handlers and their router exist in compute/functions.rs, but the
# router is never mounted in main.rs, so none of these are live today:
POST /v1/functions/deploy
POST /v1/functions/invoke/:name
GET /v1/functionsSee JavaScript Functions for current status before relying on this.
Realtime
# WebSocket connection (subscribe/unsubscribe protocol, see above)
WS /v1/realtime
# SSE stream per collection
GET /v1/stream/:collection🎯 Common Use Cases
Use Case 1: REST API Backend
// server.js
const express = require('express');
const axios = require('axios');
const app = express();
app.use(express.json());
// Create user
app.post('/users', async (req, res) => {
const response = await axios.post(
'http://localhost:3000/v1/push/users',
req.body
);
res.json(response.data);
});
// Get user
app.get('/users/:id', async (req, res) => {
const response = await axios.get(
`http://localhost:3000/v1/query/users/${req.params.id}`
);
res.json(response.data);
});
app.listen(4000);Use Case 2: Python Client
# client.py
import requests
STACKHOUSE_URL = "http://localhost:8080"
# Insert data
response = requests.post(
f"{STACKHOUSE_URL}/v1/push/products",
json={
"name": "Laptop",
"price": 999.99,
"in_stock": True
}
)
print(response.json())
# Query data
response = requests.get(f"{STACKHOUSE_URL}/v1/query/products")
print(response.json())Use Case 3: Realtime Dashboard
<!-- dashboard.html -->
<!DOCTYPE html>
<html>
<head>
<title>Stackhouse Dashboard</title>
</head>
<body>
<h1>Live Users</h1>
<ul id="users"></ul>
<script>
const ws = new WebSocket('ws://localhost:3000/v1/realtime');
const userList = document.getElementById('users');
ws.onopen = () => {
// Subscribe to the users table (real protocol: lowercase "subscribe")
ws.send(JSON.stringify({
type: 'subscribe',
table: 'users',
event: '*'
}));
// Load existing users
fetch('http://localhost:3000/v1/query/users')
.then(r => r.json())
.then(data => {
data.data.forEach(user => addUser(user));
});
};
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.type === 'INSERT') {
addUser(msg.record);
}
};
function addUser(user) {
const li = document.createElement('li');
li.textContent = `${user.name} (age: ${user.age})`;
userList.appendChild(li);
}
</script>
</body>
</html>🔍 Troubleshooting
Problem: Port already in use
# Use a different port
stackhouse serve --port 8080
# Or find and kill the process
# On Linux/Mac:
lsof -ti:3000 | xargs kill -9
# On Windows:
netstat -ano | findstr :3000
taskkill /PID <PID> /FProblem: Server won't start (missing config)
stackhouse serve requires a reachable STACKHOUSE_URL (Postgres connection string) and STACKHOUSE_JWT_SECRET. There is no local file-based storage mode — even --memory uses an isolated Postgres schema, not an embedded/on-disk file.
STACKHOUSE_URL=postgres://postgres:password@localhost:5432/stackhouse \
STACKHOUSE_JWT_SECRET=some-long-dev-secret \
stackhouse serveProblem: Can't connect
# Verify server is running
curl http://localhost:3000/health
# Check firewall settings
# Try connecting with telnet
telnet localhost 3000Log level is currently fixed at INFO in main.rs (FmtSubscriber::builder().with_max_level(Level::INFO)) — there is no --log-level flag or RUST_LOG-driven verbosity today, despite what older docs may say.
📚 Next Steps
Now that you have Stackhouse running:
-
Explore the Dashboard
- Visit http://localhost:3000/explore
- Try the visual query builder
- Monitor real-time stats
-
Build Something
- Architecture Guide - Understand the system
- Schema Evolution - Master auto-schema
- Vector Search - Add AI capabilities
-
Go Production
- Deployment Guide - Deploy to production
- Performance Tuning - Optimize your setup
- Monitoring - Set up observability
💡 Pro Tips
Tip 1: Use Environment Variables
Real env vars (stackhouse/src/cli/mod.rs ServeArgs, stackhouse/src/main.rs): STACKHOUSE_URL, STACKHOUSE_PORT, STACKHOUSE_HOST, STACKHOUSE_MEMORY, STACKHOUSE_JWT_SECRET, STACKHOUSE_STORAGE_PATH, STACKHOUSE_CORS_ALLOWED_ORIGINS. There is no STACKHOUSE_DB_PATH or STACKHOUSE_LOG_LEVEL.
# .env file
STACKHOUSE_URL=postgres://postgres:password@localhost:5432/stackhouse
STACKHOUSE_PORT=3000
STACKHOUSE_JWT_SECRET=some-long-dev-secret
# Load and run
export $(cat .env | xargs)
stackhouse serveTip 2: CORS Is Opt-In, Not On By Default
# There is no CORS-related CLI flag — set the allowlist via env var.
# An empty/unset allowlist means cross-origin requests are rejected.
STACKHOUSE_CORS_ALLOWED_ORIGINS="http://localhost:8080,https://example.com" \
stackhouse serveTip 3: Use Batch Inserts
# Faster than individual inserts
curl -X POST http://localhost:3000/v1/push/users/batch \
-H "Content-Type: application/json" \
-d '[
{"name": "User 1", "age": 25},
{"name": "User 2", "age": 30},
{"name": "User 3", "age": 35}
]'Tip 4: Monitor Performance
# Check table stats
curl http://localhost:3000/v1/tables/users
# Response includes:
# - Row count
# - Size on disk
# - Indexes
# - Last update timeCongratulations! 🎉 You now have Stackhouse running and ready to use.
Next: Learn about the Architecture to understand how everything works under the hood.