Skip to content

Quick Start

Get a REST API, MCP tools, and an OpenAPI spec from your database in under 5 minutes.

  • A stable Rust toolchain (edition 2024) — or use Nix
  • A running PostgreSQL 17 or 18 instance (or use SQLite for zero-dependency testing)
Terminal window
# Build the CLI
cargo build --release --bin pgvis
# Point it at your database
export PGVIS_DSN="postgres://user:password@localhost/mydb"
# Run every request as a low-privilege role...
export PGVIS_ANON_ROLE="web_anon"
./target/release/pgvis serve
# ...or, for local experiments only, as the DSN's own role
./target/release/pgvis serve --insecure-no-auth

pgvis introspects all tables, views, and functions in the public schema and starts serving on http://localhost:3000 (it binds 127.0.0.1 by default; pass --bind 0.0.0.0:3000 to expose it).

pgvis serve refuses to start with neither a jwt_secret nor an anon_role, because every request would then run as the DSN’s own role — often the table owner, which bypasses Row-Level Security. Set anon_role (and, for authenticated users, jwt_secret) as described in the Configuration reference, or pass --insecure-no-auth to accept that explicitly. The web_anon role above must exist in your database and be granted access to the tables you want to expose:

CREATE ROLE web_anon NOLOGIN;
GRANT web_anon TO "user"; -- the DSN's login role must be able to switch to it
GRANT USAGE ON SCHEMA public TO web_anon;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO web_anon;
-- for the write examples below, also e.g.:
-- GRANT INSERT ON items TO web_anon;

If your server requires TLS, add ?sslmode=require to the DSN; certificates are verified against the system trust store (or SSL_CERT_FILE).

Using the binary built in Option A:

Terminal window
# Use any existing SQLite database
export PGVIS_DSN="sqlite:./mydata.db"
./target/release/pgvis serve --schema main --insecure-no-auth

pgvis auto-detects SQLite from the DSN. SQLite has no roles, so --insecure-no-auth is the usual choice for local files. Its tables live in the main schema, so pass --schema main and use /api/main/<table> in place of /api/public/<table> in the examples below.

Terminal window
curl "http://localhost:3000/api/public/users"

Response:

[
{"id": 1, "name": "Alice", "email": "alice@example.com"},
{"id": 2, "name": "Bob", "email": "bob@example.com"}
]
Terminal window
curl "http://localhost:3000/api/public/users?select=id,name"
Terminal window
# Exact match
curl "http://localhost:3000/api/public/users?name=eq.Alice"
# Numeric comparison
curl "http://localhost:3000/api/public/items?price=gte.100"
# Pattern matching
curl "http://localhost:3000/api/public/items?name=ilike.*widget*"
Terminal window
# Order by price descending, take first 10
curl "http://localhost:3000/api/public/items?order=price.desc&limit=10"
# Cursor-based pagination (efficient for large datasets)
curl "http://localhost:3000/api/public/items?order=id.asc&cursor_column=id&cursor_value=42&limit=10"
Terminal window
# Get orders with their items (via foreign key)
curl "http://localhost:3000/api/public/orders?select=id,total,items(*)"
Terminal window
curl -X POST "http://localhost:3000/api/public/items" \
-H "Content-Type: application/json" \
-H "Prefer: return=representation" \
-d '{"name": "Widget", "price": 9.99}'
Terminal window
curl -X POST "http://localhost:3000/api/public/rpc/add" \
-H "Content-Type: application/json" \
-d '{"a": 2, "b": 3}'

Add to your Claude Desktop MCP configuration (claude_desktop_config.json):

{
"mcpServers": {
"mydb": {
"command": "./target/release/pgvis",
"args": ["--dsn", "postgres://user@localhost/mydb", "mcp"]
}
}
}

Claude can now query your database using natural language — pgvis generates typed tools for every table and function. MCP tool calls run as anon_role (pass "env": {"PGVIS_ANON_ROLE": "web_anon"} in the entry); with no anon_role and no jwt_secret they run as the DSN’s own role.

{
"mcpServers": {
"mydb": {
"command": "./target/release/pgvis",
"args": ["--dsn", "postgres://user@localhost/mydb", "mcp", "--read-only"]
}
}
}
Terminal window
PGVIS_ANON_ROLE=web_anon pgvis --dsn "postgres://user@localhost/mydb" serve --mcp-http
# MCP available at POST /mcp
Terminal window
# Print the OpenAPI 3.0 spec to stdout
pgvis --dsn "postgres://user@localhost/mydb" openapi > openapi.json
Terminal window
# Dump the introspected schema as JSON (tables, columns, relationships, routines)
pgvis --dsn "postgres://user@localhost/mydb" inspect
Terminal window
# Development shell with all dependencies
nix develop
# Or build the binary directly
nix build
./result/bin/pgvis --dsn "postgres://user@localhost/mydb" serve --insecure-no-auth

pgvis picks up DDL without a restart. At the end of a migration run:

NOTIFY pgrst, 'reload schema';

or send SIGUSR1 to the pgvis serve process.