Getting Started
Install PowDB, create a table, insert data, query it, and run in server mode. Takes about 10 minutes.
1Install
The easiest way to install PowDB is from crates.io:
cargo install powdb-cli
cargo install powdb-server
Or build from source (requires Rust 1.93+; install from rustup.rs if needed):
git clone https://github.com/ZVN-DEV/powdb.git
cd powdb
cargo build --release
Check that installation worked:
powdb-cli --version
2Start the REPL
Launch the interactive PowQL shell:
powdb-cli
# or from source:
cargo run --release -p powdb-cli
You should see:
Data is stored in ./powdb_data/ by default. Change it with --data-dir:
powdb-cli --data-dir ./my_project_data
3Create a Table
PowDB uses the type keyword to define tables. Fields prefixed with required cannot be null.
powql> type User { required name: str, required email: str, age: int }
No CREATE TABLE, no column types in parentheses. Fields have a name and a type, separated by a colon.
Supported types: str, int, float, bool, datetime, uuid, bytes.
4Insert Data
Insert rows with the insert keyword. Fields are assigned with :=.
powql> insert User { name := "Alice", email := "alice@example.com", age := 30 }
Add a few more people:
powql> insert User { name := "Bob", email := "bob@example.com", age := 25 }
powql> insert User { name := "Charlie", email := "charlie@example.com", age := 35 }
powql> insert User { name := "Diana", email := "diana@example.com", age := 28 }
powql> insert User { name := "Eve", email := "eve@example.com", age := 22 }
powql> insert User { name := "Frank", email := "frank@example.com", age := 40 }
Fields without required can be omitted -- they default to null:
powql> insert User { name := "Grace", email := "grace@example.com" }
5Query Basics
Select all rows
Just type the table name:
powql> User
Filter rows
Use filter with a condition. Fields are referenced with a dot prefix:
powql> User filter .age > 25
Project specific fields
Use { } braces to select which fields to return:
powql> User filter .age > 25 { .name, .age }
6Sorting and Limiting
PowQL operations chain left to right in a pipeline. Add order and limit:
powql> User order .age desc limit 3 { .name, .age }
7Aggregations
PowQL wraps aggregate functions around the query pipeline.
powql> count(User)
7
powql> count(User filter .age > 25)
4
powql> avg(User { .age })
30
powql> sum(User filter .age > 25 { .age })
133
Other aggregate functions: min(), max().
8Updates
Use update after an optional filter to modify rows:
powql> User filter .name = "Alice" update { age := 31 }
Verify it worked:
powql> User filter .name = "Alice" { .name, .age }
You can use expressions that reference the current row value:
powql> User filter .name = "Bob" update { age := .age + 1 }
9Create an Index
Indexes speed up lookups on frequently queried columns:
powql> alter User add index .email
Indexed columns are used automatically for point lookups and range scans -- no query hints needed.
10Delete
Use delete after a filter to remove matching rows:
powql> User filter .age < 25 delete
To delete all rows in a table: User delete
11Transactions
Statements run in autocommit by default -- each one is durable on its own. For atomic multi-statement changes (and much faster bulk loads), use explicit transactions:
powql> begin
powql> insert User { name := "Alice", email := "alice@example.com", age := 30 }
powql> insert User { name := "Bob", email := "bob@example.com", age := 25 }
powql> commit
Everything between begin and commit applies atomically -- either all statements succeed or none do. Use rollback instead of commit to discard uncommitted changes. Batching writes in a transaction shares a single WAL fsync at commit, which makes bulk loads roughly 50x faster with identical durability.
Full details: Transactions in the PowQL reference.
12Server Mode
For multi-client access, run PowDB as a server.
Start the server
powdb-server --port 5433 --data-dir ./powdb_data
Connect from a client
powdb-cli --remote localhost:5433
From here, the same PowQL statements work as embedded mode.
TypeScript client
import { Client } from "@zvndev/powdb-client";
const client = await Client.connect({ host: "localhost", port: 5433 });
const result = await client.query("User filter .age > 25 { .name, .age }");
if (result.kind === "rows") console.table(result.rows);
Password authentication
# Start the server with a password
POWDB_PASSWORD=mysecret powdb-server
# Connect with the password
powdb-cli --remote localhost:5433 --password mysecret
Multi-user authentication
PowDB also supports named users with roles (admin, readwrite, readonly), stored as argon2id hashes in the data directory. Once any user is defined, the shared password is no longer used:
# Create users (role defaults to readwrite)
powdb-cli --data-dir ./powdb_data useradd alice --role admin --password s3cret
powdb-cli --data-dir ./powdb_data useradd bob --role readonly --password hunter2
# Connect as a named user
powdb-cli --remote localhost:5433 --user alice --password s3cret
For containerized first boots, set POWDB_ADMIN_USER and POWDB_ADMIN_PASSWORD and the server bootstraps an admin user on startup -- no CLI needed. See Multi-user authentication for the full workflow.
What's Next
This tutorial covered tables, inserts, queries, aggregates, updates, indexes, deletes, and server mode. PowDB supports much more, including joins, group by, subqueries, materialized views, window functions, and set operations.
See the full language reference: PowQL Reference