Quick Start
Get a REST API, MCP tools, and an OpenAPI spec from your database in under 5 minutes.
Prerequisites
Section titled “Prerequisites”- A stable Rust toolchain (edition 2024) — or use Nix
- A running PostgreSQL 17 or 18 instance (or use SQLite for zero-dependency testing)
Option A: PostgreSQL
Section titled “Option A: PostgreSQL”# Build the CLIcargo build --release --bin pgvis
# Point it at your databaseexport 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-authpgvis 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 itGRANT 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).
Option B: SQLite (No Server Required)
Section titled “Option B: SQLite (No Server Required)”Using the binary built in Option A:
# Use any existing SQLite databaseexport PGVIS_DSN="sqlite:./mydata.db"./target/release/pgvis serve --schema main --insecure-no-authpgvis 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.
First Requests
Section titled “First Requests”List rows
Section titled “List rows”curl "http://localhost:3000/api/public/users"Response:
[ {"id": 1, "name": "Alice", "email": "alice@example.com"}, {"id": 2, "name": "Bob", "email": "bob@example.com"}]Select specific columns
Section titled “Select specific columns”curl "http://localhost:3000/api/public/users?select=id,name"Filter rows
Section titled “Filter rows”# Exact matchcurl "http://localhost:3000/api/public/users?name=eq.Alice"
# Numeric comparisoncurl "http://localhost:3000/api/public/items?price=gte.100"
# Pattern matchingcurl "http://localhost:3000/api/public/items?name=ilike.*widget*"Order and paginate
Section titled “Order and paginate”# Order by price descending, take first 10curl "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"Embed related resources
Section titled “Embed related resources”# Get orders with their items (via foreign key)curl "http://localhost:3000/api/public/orders?select=id,total,items(*)"Create a row
Section titled “Create a row”curl -X POST "http://localhost:3000/api/public/items" \ -H "Content-Type: application/json" \ -H "Prefer: return=representation" \ -d '{"name": "Widget", "price": 9.99}'Call a function
Section titled “Call a function”curl -X POST "http://localhost:3000/api/public/rpc/add" \ -H "Content-Type: application/json" \ -d '{"a": 2, "b": 3}'MCP for AI Agents
Section titled “MCP for AI Agents”Claude Desktop (stdio transport)
Section titled “Claude Desktop (stdio transport)”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.
Read-only mode (safer for LLMs)
Section titled “Read-only mode (safer for LLMs)”{ "mcpServers": { "mydb": { "command": "./target/release/pgvis", "args": ["--dsn", "postgres://user@localhost/mydb", "mcp", "--read-only"] } }}HTTP transport (alongside REST)
Section titled “HTTP transport (alongside REST)”PGVIS_ANON_ROLE=web_anon pgvis --dsn "postgres://user@localhost/mydb" serve --mcp-http# MCP available at POST /mcpOpenAPI Document
Section titled “OpenAPI Document”# Print the OpenAPI 3.0 spec to stdoutpgvis --dsn "postgres://user@localhost/mydb" openapi > openapi.jsonInspect Schema Cache
Section titled “Inspect Schema Cache”# Dump the introspected schema as JSON (tables, columns, relationships, routines)pgvis --dsn "postgres://user@localhost/mydb" inspectNix Users
Section titled “Nix Users”# Development shell with all dependenciesnix develop
# Or build the binary directlynix build./result/bin/pgvis --dsn "postgres://user@localhost/mydb" serve --insecure-no-authReloading After Schema Changes
Section titled “Reloading After Schema Changes”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.