Try pgview
This guide walks you through spinning up a local PostgreSQL instance with realistic seed data and exploring every feature of pgview hands-on. You will need a container runtime and the pgview binary — nothing else.
Total setup time is roughly 10–15 minutes. All resources run locally; nothing is sent to an external service.
Prerequisites
1 — Container runtime
You need one of the following to run PostgreSQL locally. Install the one that fits your OS:
# macOS — install via Homebrew
brew install --cask docker
# Linux — follow the official script
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER # log out and back in after this
# Verify
docker --version
Then open the Docker Desktop application once so the daemon starts.
# Colima is a lightweight Docker runtime for macOS — no GUI required
brew install colima docker
# Start the VM (arm64 for Apple Silicon, x86_64 for Intel)
colima start
# Verify
docker --version
Colima starts a lightweight Lima VM and exposes the Docker socket. It uses fewer resources than Docker Desktop and has no paid tier.
# macOS
brew install podman
podman machine init
podman machine start
# Linux
sudo apt-get install -y podman # Debian/Ubuntu
sudo dnf install -y podman # Fedora/RHEL
# Alias docker → podman if you prefer
alias docker=podman
# Verify
podman --version
Podman is daemonless and runs containers rootless by default. All docker commands in this guide work identically with podman.
2 — Docker Hub account (free)
The PostgreSQL image is pulled from Docker Hub. Anonymous pulls are rate-limited, so a free account is recommended.
# Log in once — credentials are cached on disk
docker login
Create a free account at hub.docker.com if you do not have one yet.
3 — pgview binary
Covered in the Get pgview section below — no installation needed, it is a single static binary.
Start PostgreSQL
Run a PostgreSQL 16 container with a known user, password, and database name.
docker run -d \
--name pgview-demo \
-e POSTGRES_USER=demo \
-e POSTGRES_PASSWORD=demo1234 \
-e POSTGRES_DB=demodb \
-p 5432:5432 \
postgres:16-alpine
postgres:16-alpine image
ships multi-arch manifests — the right image is pulled automatically. No flags needed.
Wait a few seconds for the container to become healthy, then verify:
docker exec pgview-demo pg_isready -U demo -d demodb
# Expected: localhost:5432 - accepting connections
-p 5433:5432. Then pass
pgview -url localhost:5433 later.
When you are done testing, stop and remove the container:
docker stop pgview-demo && docker rm pgview-demo
Seed the Database
Connect to the container and create a realistic demo schema with four related tables and several hundred rows.
docker exec -i pgview-demo psql -U demo -d demodb <<'SQL'
-- ─────────────────────────────────────────────────────────────
-- Schema: demo
-- ─────────────────────────────────────────────────────────────
CREATE SCHEMA IF NOT EXISTS demo;
-- customers
CREATE TABLE demo.customers (
id bigserial PRIMARY KEY,
name text NOT NULL,
email text NOT NULL UNIQUE,
status text NOT NULL DEFAULT 'active',
created_at timestamptz NOT NULL DEFAULT now(),
tags text[]
);
COMMENT ON COLUMN demo.customers.tags IS 'freeform labels for segmentation';
CREATE INDEX idx_customers_status ON demo.customers (status);
ALTER TABLE demo.customers
ADD CONSTRAINT customers_status_chk
CHECK (status IN ('active', 'inactive', 'suspended'));
-- products
CREATE TABLE demo.products (
id serial PRIMARY KEY,
name text NOT NULL,
category text NOT NULL,
price numeric(10,2)NOT NULL,
stock_qty int NOT NULL DEFAULT 0
);
CREATE INDEX idx_products_category ON demo.products (category);
-- orders
CREATE TABLE demo.orders (
id bigserial PRIMARY KEY,
customer_id bigint NOT NULL REFERENCES demo.customers(id),
total numeric(10,2)NOT NULL,
status text NOT NULL DEFAULT 'pending',
created_at timestamptz NOT NULL DEFAULT now(),
notes jsonb
);
CREATE INDEX idx_orders_customer ON demo.orders (customer_id);
CREATE INDEX idx_orders_status ON demo.orders (status);
ALTER TABLE demo.orders
ADD CONSTRAINT orders_status_chk
CHECK (status IN ('pending', 'paid', 'shipped', 'cancelled'));
-- order_items
CREATE TABLE demo.order_items (
id bigserial PRIMARY KEY,
order_id bigint NOT NULL REFERENCES demo.orders(id),
product_id int NOT NULL REFERENCES demo.products(id),
quantity int NOT NULL DEFAULT 1,
unit_price numeric(10,2)NOT NULL
);
-- ─────────────────────────────────────────────────────────────
-- Seed data
-- ─────────────────────────────────────────────────────────────
INSERT INTO demo.customers (name, email, status, tags) VALUES
('Alice Nguyen', 'alice@example.com', 'active', '{vip,newsletter}'),
('Bob Okonkwo', 'bob@example.com', 'active', '{newsletter}'),
('Carol Singh', 'carol@example.com', 'active', '{vip}'),
('David Rossi', 'david@example.com', 'inactive', NULL),
('Eve Martínez', 'eve@example.com', 'active', '{vip,newsletter}'),
('Frank Dubois', 'frank@example.com', 'suspended', NULL),
('Grace Kim', 'grace@example.com', 'active', '{newsletter}'),
('Hiro Tanaka', 'hiro@example.com', 'active', '{vip}'),
('Ingrid Svensson','ingrid@example.com', 'active', '{newsletter}'),
('James O''Brien', 'james@example.com', 'inactive', NULL);
INSERT INTO demo.products (name, category, price, stock_qty) VALUES
('Wireless Keyboard', 'Electronics', 79.99, 50),
('USB-C Hub', 'Electronics', 49.99, 120),
('Mechanical Keyboard', 'Electronics', 149.00, 30),
('Desk Lamp', 'Office', 35.50, 200),
('Notebook A5', 'Stationery', 8.99, 500),
('Ergonomic Mouse', 'Electronics', 59.00, 75),
('Monitor Stand', 'Office', 89.00, 40),
('Cable Organiser', 'Office', 14.99, 300);
INSERT INTO demo.orders (customer_id, total, status, notes) VALUES
(1, 129.98, 'paid', '{"source":"web","promo":"SUMMER10"}'),
(2, 49.99, 'shipped', NULL),
(3, 238.00, 'paid', '{"source":"app"}'),
(1, 35.50, 'pending', NULL),
(5, 59.00, 'paid', '{"source":"web"}'),
(7, 8.99, 'cancelled', NULL),
(8, 168.99, 'shipped', '{"source":"app","gift":true}'),
(9, 14.99, 'paid', NULL);
INSERT INTO demo.order_items (order_id, product_id, quantity, unit_price) VALUES
(1, 1, 1, 79.99), (1, 2, 1, 49.99),
(2, 2, 1, 49.99),
(3, 3, 1, 149.00), (3, 7, 1, 89.00),
(4, 4, 1, 35.50),
(5, 6, 1, 59.00),
(6, 5, 1, 8.99),
(7, 3, 1, 149.00), (7, 6, 1, 19.99),
(8, 8, 1, 14.99);
ANALYZE;
SQL
Verify the seed data was inserted:
docker exec pgview-demo psql -U demo -d demodb \
-c "SELECT schemaname, tablename, n_live_tup FROM pg_stat_user_tables WHERE schemaname = 'demo' ORDER BY tablename;"
You should see four rows with non-zero n_live_tup values.
Get pgview
pgview is a single static binary — no runtime dependencies, no installer. Download the build that matches your OS and chip from the v0.4.1 release page.
| Platform | Binary to download |
|---|---|
| macOS — Apple Silicon (M1/M2/M3/M4) | pgview-darwin-arm64 |
| macOS — Intel | pgview-darwin-amd64 |
| Linux — x86-64 | pgview-linux-amd64 |
# Download
curl -L https://github.com/sibasismukherjee/pgview/releases/download/v0.4.1/pgview-darwin-arm64 \
-o pgview
# Make executable
chmod +x pgview
# Optional: move to PATH so you can run it from anywhere
mv pgview /usr/local/bin/pgview
# Verify
pgview -version
xattr -d com.apple.quarantine pgview
then try again. Alternatively: System Settings → Privacy & Security → click "Allow Anyway".
# Download
curl -L https://github.com/sibasismukherjee/pgview/releases/download/v0.4.1/pgview-darwin-amd64 \
-o pgview
chmod +x pgview
mv pgview /usr/local/bin/pgview
pgview -version
xattr -d com.apple.quarantine pgview if blocked.
# Download
curl -L https://github.com/sibasismukherjee/pgview/releases/download/v0.4.1/pgview-linux-amd64 \
-o pgview
chmod +x pgview
sudo mv pgview /usr/local/bin/pgview
pgview -version
Or build from source if you have Go 1.22+:
git clone https://github.com/sibasismukherjee/pgview.git
cd pgview
make build # produces ./pgview
Connect to PostgreSQL
Run pgview and point it at the container you started earlier:
# One-liner with all flags
pgview -url localhost:5432 -username demo -password demo1234 -dbname demodb -sslmode disable
Or run interactively and enter each value at the prompt:
pgview
# Connection URL (host:port): localhost:5432
# Username: demo
# Database [postgres]: demodb
# Password: demo1234 ← not echoed
pgview opens directly on the Table List. You should see the four
tables in the demo schema: customers, order_items,
orders, and products.
demo@demodb · localhost — a quick reminder
of which database you are connected to.
Feature 1 — Table List
The table list is the home screen. Use ↑ / ↓ to move the selection. Try these keys:
demo.orders and press i. The top-right panel
shows the estimated row count, primary key column(s), and index count — fetched
from pg_class without scanning the table.
demo.orders to open the 4-tab schema panel.
Covered in detail in the Schema Browser section.
Feature 2 — Fuzzy Table Search
From the table list, press / to open the full-screen fuzzy finder. It searches all schemas simultaneously.
ord — pgview instantly filters to
demo.orders and demo.order_items. Matched characters
are highlighted in blue.
cu — narrows to demo.customers.
Press Enter to jump straight into its data view.
oi ranks order_items above orders.
Feature 3 — Data View & Filter DSL
Navigate to demo.orders (fuzzy search ord then
Enter). The data view shows 200 rows per page with type-aware colouring:
numeric values are green, timestamps orange, NULL is dim gray, booleans are
teal/red.
status=paid # exact match
status!=cancelled # negation
total>100 # numeric comparison
total>=50 status=shipped # AND — two terms
customers.
Navigate to demo.customers and try:
tags=vip # element-wise: ANY(tags) = 'vip'
tags=%newsletter% # substring per element
orders.
Navigate back to demo.orders and try:
notes=%web% # matches rows where notes contains "web"
demo.order_items), swipe horizontally with two fingers or use
WheelLeft / WheelRight to pan columns.
Feature 4 — Schema Browser
From the table list or data view, press d to open the 4-tab schema panel.
Try it on demo.orders, which has a FK, a CHECK constraint, two indexes,
and a rich DDL.
pg_catalog.format_type),
nullability, and default. Notice total is numeric(10,2)
and notes is jsonb.
orders_pkey (PRIMARY, btree), idx_orders_customer,
and idx_orders_status with their full pg_get_indexdef
definitions.
orders_pkey (PRIMARY KEY), the FK to customers,
and the orders_status_chk CHECK constraint with its expression.
CREATE TABLE statement — copy-pasteable into
psql or a migration file. Scroll down to see the standalone
CREATE INDEX lines.
Feature 5 — Row Viewer & Inline Editor
Navigate into demo.customers, select any row, and press f.
status
field. The input bar at the bottom pre-fills with the current value
(active). Change it to inactive and press Enter.
The field is now highlighted in teal with an (edited) marker.
UPDATE demo.customers SET status = 'inactive' WHERE id = <original_id>
The data view refreshes and the change is visible immediately.
NULL in the input bar to set a field to SQL NULL
(any case works). Useful for clearing the tags array.
WHERE clause, so the right row is always
targeted.
Feature 6 — SQL Editor & Templates
Press e from any view to open the full-screen SQL editor. From the data view the editor is pre-filled with the last query.
SELECT c.name, COUNT(o.id) AS order_count, SUM(o.total) AS total_spent
FROM demo.customers c
LEFT JOIN demo.orders o ON o.customer_id = c.id
GROUP BY c.name
ORDER BY total_spent DESC NULLS LAST;
Press Ctrl+E to run. Results appear in the data view.
SELECT * FROM demo. and press Tab.
pgview suggests table names. After selecting one, type a column name prefix and
press Tab again for column suggestions in context.
demo.orders data view, then press
Ctrl+T. The left panel fills with pre-built queries using the
real column names of orders. Select INSERT and press
Enter — a complete INSERT template with all columns drops into the
editor.
Feature 7 — Export to CSV / JSON
From any data view (table browse or SQL results), press E (Shift+E) to start the interactive export flow.
export format: prompt type csv and press
Enter.
~/export_orders_<timestamp>.csv.
Press Enter to accept, or edit it first.
LIMIT / OFFSET.
With the status=paid filter active, only paid orders are written.
head -5 ~/export_orders_*.csv
You'll see the header row followed by data rows; NULL fields are empty strings.
json format. NULL becomes null in the
JSON output.
Feedback & Roadmap
Thank you for trying pgview! Your feedback shapes what gets built next.
Report an Issue
Found a bug, unexpected behaviour, or something that felt off? Open an issue on GitHub — include your OS, the binary you used, and steps to reproduce.
Open an Issue →See the Roadmap
Curious what's coming next? The project board tracks every planned enhancement — EXPLAIN panel, clipboard copy, connection manager, inline cell editing, and more.
View Roadmap →
Feature requests are also welcome as GitHub Issues — browse the
open issues
first to see if your idea already has a thread, or open a new one with
the enhancement label.
Clean up
When you are done, remove the demo container:
docker stop pgview-demo && docker rm pgview-demo