Skip to main content

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 --build

This 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-secret

Real 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/health

Response:

{
  "status": "healthy",
  "database": "connected"
}

Step 3: Open the Dashboard

Open browser: http://localhost:3000/explore

You'll see the Stackhouse Explorer:

Tables

(empty)

Query

Builder

Stats

Overview

💡 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:

  1. Stackhouse receives the JSON.
  2. Analyzes the structure: name: String → TEXT, email: String → TEXT, age: Number → BIGINT.
  3. 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;
  4. Inserts the data.
  5. 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/1

Response:

{
  "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/execute

Schema & Metadata

# List all tables
GET    /v1/tables

# Get table stats / drop a table
GET    /v1/tables/:collection
DELETE /v1/tables/:collection

There 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/info

There 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/functions

See 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> /F

Problem: 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 serve

Problem: Can't connect

# Verify server is running
curl http://localhost:3000/health

# Check firewall settings
# Try connecting with telnet
telnet localhost 3000

Log 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:

  1. Explore the Dashboard

  2. Build Something

  3. Go Production


💡 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 serve

Tip 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 serve

Tip 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 time

Congratulations! 🎉 You now have Stackhouse running and ready to use.

Next: Learn about the Architecture to understand how everything works under the hood.