Schema Explorer
The Schema Explorer gives you read-only access to your database structure directly from the terminal. No need to switch to pgAdmin, MySQL Workbench, or Azure Data Studio. Browse tables, views, indexes, foreign keys, functions, procedures, types, and triggers without leaving your workflow.
What It Shows
The explorer queries your database's system catalogs to reveal its structure:
| Category | Information |
|---|---|
| Tables | Columns, data types, row estimates, indexes, foreign keys |
| Views | Columns, definition SQL, whether it is updatable |
| Indexes | Table, columns, unique/primary status |
| Foreign Keys | Source and target tables, column mappings, cascade rules |
| Functions | Parameters, return type, definition |
| Procedures | Parameters, definition |
| Types | Enum values, composite attributes, domain base types |
| Triggers | Table, timing (BEFORE/AFTER/INSTEAD OF), events, definition |
Two more categories, locks and connections, describe runtime state rather than schema. They are counted in the --json overview and reachable from the SDK, but they have no CLI listing and no TUI screen.
Which catalogs each dialect reads:
| Dialect | Catalogs |
|---|---|
| PostgreSQL | information_schema for tables, columns, views, FKs, and parameters; pg_catalog (pg_proc, pg_type, pg_enum, pg_indexes, pg_locks, pg_stat_activity) for everything it cannot express |
| MySQL | information_schema throughout, plus performance_schema.metadata_locks for locks |
| MSSQL | sys.* catalog views (sys.tables, sys.views, sys.procedures, sys.types, sys.indexes, sys.foreign_keys, sys.triggers) |
| SQLite | sqlite_master for object names and DDL, PRAGMA table_info / index_info / foreign_key_list for the rest |
noorm's own tracking objects are filtered out: PostgreSQL and MSSQL exclude the noorm schema in the catalog query, MySQL and SQLite drop names starting with __noorm_. Set the log level to verbose to include them.
Accessing the Explorer
Terminal UI
From the home screen, press d to enter the Database menu, then x to explore:
Home → [d] Database → [x] ExploreThe Database menu:
┌─ Database ──────────────────────────────────────────────────┐
│ │
│ [c] create Create the database for this config │
│ [d] destroy Drop the database for this config │
│ [x] explore Browse tables, views, indexes │
│ [w] wipe Truncate all data (keep schema) │
│ [t] teardown Drop all objects │
│ [r] transfer Move data between configs │
│ │
└─────────────────────────────────────────────────────────────┘The SQL terminal is not on this menu. Press Shift+Q from anywhere, or run noorm sql repl. See SQL Terminal.
Scripted Usage
The same subcommands run non-interactively — add --json to get machine-readable output:
noorm db explore # Overview
noorm db explore tables # List tables
noorm db explore tables detail users # Table detail
noorm db explore fks # List foreign keys
noorm db explore triggers # List triggers
noorm db explore tables --schema app # Restrict to one schema
noorm db explore --json > schema.json # JSON outputThe subcommands are tables, views, procedures, functions, types, indexes, fks, and triggers. Every one accepts --schema except on SQLite, which has no schemas and rejects the flag rather than returning an empty list. tables, views, procedures, functions, types, and triggers also take a detail <name> subcommand.
Overview Screen
The overview shows counts of all database objects at a glance:
┌─ DB Explore ────────────────────────────────────────────────┐
│ │
│ Config: dev (postgres) │
│ Database: myapp_development │
│ Total Objects: 47 │
│ │
│ [1] Tables 12 │
│ [2] Views 3 │
│ [3] Procedures 0 │
│ [4] Functions 5 │
│ [5] Types 2 │
│ [6] Indexes 18 │
│ [7] Foreign Keys 7 │
│ │
└─────────────────────────────────────────────────────────────┘
[1-7] Navigate [Esc] BackPress the number key or hotkey to drill into any category. Categories with zero items appear dimmed and cannot be selected.
| Key | Hotkey | Category |
|---|---|---|
1 | t | Tables |
2 | v | Views |
3 | p | Procedures |
4 | f | Functions |
5 | y | Types |
6 | i | Indexes |
7 | k | Foreign Keys |
The TUI overview lists these seven. noorm db explore on the command line prints a Triggers count as well, and --json carries all ten counters including locks and connections. The Total Objects figure sums every counter the overview computed, so it can exceed the seven rows on screen when triggers, locks, or connections are present.
Browsing Tables
Press 1 or t from the overview to see all tables:
┌─ Tables (12) ───────────────────────────────────────────────┐
│ │
│ / Filter tables... │
│ │
│ 1. > public.users 8 columns, ~1.5K rows │
│ 2. public.posts 5 columns, ~42K rows │
│ 3. public.comments 4 columns, ~128K rows │
│ 4. public.notifications 6 columns, ~8.9K rows │
│ 5. public.sessions 3 columns, ~2.1K rows │
│ │
│ ... 7 more │
│ │
└─────────────────────────────────────────────────────────────┘
[Enter] View detail [Esc] BackThe list is searchable. Press / to focus the filter field and type to narrow results. Press 1-9 for quick selection.
Table Detail
Select a table to see its full schema:
┌─ Table ─────────────────────────────────────────────────────┐
│ │
│ public.users ~1,500 rows │
│ │
│ Columns (8) │
│ ───────────────────────────────────────────────────────── │
│ * id uuid NOT NULL │
│ email varchar(255) NOT NULL │
│ name varchar(100) NULL │
│ password_hash text NOT NULL │
│ role user_role NOT NULL DEFAULT 'user' │
│ active boolean NOT NULL DEFAULT true │
│ created_at timestamptz NOT NULL DEFAULT now() │
│ updated_at timestamptz NULL │
│ │
│ Indexes (2) │
│ ───────────────────────────────────────────────────────── │
│ * users_pkey (id) │
│ users_email_idx (email) UNIQUE │
│ │
│ Foreign Keys (0) │
│ │
└─────────────────────────────────────────────────────────────┘
[Esc] BackThe * marker indicates primary key columns and primary key indexes.
Browsing Views
Press 2 or v from the overview:
┌─ Views (3) ─────────────────────────────────────────────────┐
│ │
│ 1. > public.active_users 4 columns, READ-ONLY │
│ 2. public.post_stats 6 columns, READ-ONLY │
│ 3. public.user_activity 8 columns, UPDATABLE │
│ │
└─────────────────────────────────────────────────────────────┘
[Enter] View detail [Esc] BackView Detail
┌─ View ──────────────────────────────────────────────────────┐
│ │
│ public.active_users READ-ONLY │
│ │
│ Columns (4) │
│ ───────────────────────────────────────────────────────── │
│ id uuid NOT NULL │
│ email varchar(255) NOT NULL │
│ name varchar(100) NULL │
│ created_at timestamptz NOT NULL │
│ │
│ Definition │
│ ───────────────────────────────────────────────────────── │
│ SELECT id, email, name, created_at │
│ FROM users │
│ WHERE active = true │
│ │
└─────────────────────────────────────────────────────────────┘
[Esc] BackBrowsing Indexes and Foreign Keys
Indexes and foreign keys are list-only categories. They show all the information inline without a separate detail screen.
Indexes
┌─ Indexes (18) ──────────────────────────────────────────────┐
│ │
│ 1. users_pkey on users, PRIMARY │
│ 2. users_email_idx on users, UNIQUE │
│ 3. posts_pkey on posts, PRIMARY │
│ 4. posts_user_id_idx on posts │
│ 5. posts_created_at_idx on posts │
│ 6. comments_pkey on comments, PRIMARY │
│ │
│ ... 12 more │
│ │
└─────────────────────────────────────────────────────────────┘
[Esc] BackForeign Keys
┌─ Foreign Keys (7) ──────────────────────────────────────────┐
│ │
│ 1. posts_user_id_fkey │
│ posts(user_id) → users(id) ON DELETE CASCADE │
│ │
│ 2. comments_post_id_fkey │
│ comments(post_id) → posts(id) ON DELETE CASCADE │
│ │
│ 3. comments_user_id_fkey │
│ comments(user_id) → users(id) ON DELETE SET NULL │
│ │
└─────────────────────────────────────────────────────────────┘
[Esc] BackBrowsing Functions and Procedures
Functions and procedures show parameters, return types, and definitions.
Functions
┌─ Functions (5) ─────────────────────────────────────────────┐
│ │
│ 1. > public.calculate_total 2 params → numeric │
│ 2. public.get_user_posts 1 param → SETOF posts │
│ 3. public.update_timestamp 0 params → trigger │
│ │
└─────────────────────────────────────────────────────────────┘
[Enter] View detail [Esc] BackFunction Detail
┌─ Function ──────────────────────────────────────────────────┐
│ │
│ public.calculate_total → numeric │
│ │
│ Parameters (2) │
│ ───────────────────────────────────────────────────────── │
│ price numeric IN │
│ quantity integer IN │
│ │
│ Definition │
│ ───────────────────────────────────────────────────────── │
│ BEGIN │
│ RETURN price * quantity; │
│ END; │
│ │
└─────────────────────────────────────────────────────────────┘
[Esc] BackProcedures
Procedures work the same way as functions, but without a return type.
Browsing Types
Custom types are available in PostgreSQL and MSSQL. The explorer shows enum values, composite type attributes, and domain/alias base types.
┌─ Types (2) ─────────────────────────────────────────────────┐
│ │
│ 1. > public.user_role ENUM, 3 values │
│ 2. public.address COMPOSITE, 5 attributes │
│ │
└─────────────────────────────────────────────────────────────┘
[Enter] View detail [Esc] BackEnum Type Detail
┌─ Type ──────────────────────────────────────────────────────┐
│ │
│ public.user_role ENUM │
│ │
│ Values (3) │
│ ───────────────────────────────────────────────────────── │
│ user │
│ moderator │
│ admin │
│ │
└─────────────────────────────────────────────────────────────┘
[Esc] BackComposite Type Detail
┌─ Type ──────────────────────────────────────────────────────┐
│ │
│ public.address COMPOSITE │
│ │
│ Attributes (5) │
│ ───────────────────────────────────────────────────────── │
│ street varchar(200) NOT NULL │
│ city varchar(100) NOT NULL │
│ state varchar(50) NULL │
│ postal_code varchar(20) NOT NULL │
│ country varchar(100) NOT NULL │
│ │
└─────────────────────────────────────────────────────────────┘
[Esc] BackHeadless JSON Output
Use --json flag for machine-readable output. Every noorm command wraps its --json payload in an object carrying a top-level success boolean, so jq -e '.success' works the same way against all of them. A list is never emitted as a bare array; it is always named, as in { "tables": [...] }.
Overview
noorm db explore --json{
"success": true,
"tables": 12,
"views": 3,
"procedures": 0,
"functions": 5,
"types": 2,
"indexes": 18,
"foreignKeys": 7,
"triggers": 4,
"locks": 0,
"connections": 2
}Table List
noorm db explore tables --json{
"success": true,
"tables": [
{ "name": "users", "schema": "public", "columnCount": 8, "rowCountEstimate": 1500 },
{ "name": "posts", "schema": "public", "columnCount": 5, "rowCountEstimate": 42000 },
{ "name": "comments", "schema": "public", "columnCount": 4, "rowCountEstimate": 128000 }
]
}The other list subcommands follow the same shape under their own key: views, procedures, functions, types, indexes, foreignKeys (from noorm db explore fks), and triggers.
Table Detail
noorm db explore tables detail users --json{
"success": true,
"name": "users",
"schema": "public",
"columns": [
{ "name": "id", "dataType": "uuid", "isNullable": false, "isPrimaryKey": true, "ordinalPosition": 1 },
{ "name": "email", "dataType": "varchar(255)", "isNullable": false, "isPrimaryKey": false, "ordinalPosition": 2 },
{ "name": "name", "dataType": "varchar(100)", "isNullable": true, "isPrimaryKey": false, "ordinalPosition": 3 },
{ "name": "created_at", "dataType": "timestamptz", "isNullable": false, "defaultValue": "now()", "isPrimaryKey": false, "ordinalPosition": 7 }
],
"indexes": [
{ "name": "users_pkey", "columns": ["id"], "isUnique": true, "isPrimary": true },
{ "name": "users_email_idx", "columns": ["email"], "isUnique": true, "isPrimary": false }
],
"foreignKeys": [],
"rowCountEstimate": 1500
}View Detail
noorm db explore views detail active_users --json{
"success": true,
"name": "active_users",
"schema": "public",
"columns": [
{ "name": "id", "dataType": "uuid", "isNullable": false, "isPrimaryKey": false, "ordinalPosition": 1 },
{ "name": "email", "dataType": "varchar(255)", "isNullable": false, "isPrimaryKey": false, "ordinalPosition": 2 }
],
"definition": "SELECT id, email FROM users WHERE active = true",
"isUpdatable": false
}Function Detail
noorm db explore functions detail calculate_total --json{
"success": true,
"name": "calculate_total",
"schema": "public",
"parameters": [
{ "name": "price", "dataType": "numeric", "mode": "IN", "ordinalPosition": 1 },
{ "name": "quantity", "dataType": "integer", "mode": "IN", "ordinalPosition": 2 }
],
"returnType": "numeric",
"definition": "BEGIN RETURN price * quantity; END;"
}Dialect Support
The explorer works across all supported databases with appropriate translations:
| Category | PostgreSQL | MySQL | MSSQL | SQLite |
|---|---|---|---|---|
| Tables | Yes | Yes | Yes | Yes |
| Views | Yes | Yes | Yes | Yes |
| Indexes | Yes | Yes | Yes | Yes |
| Foreign Keys | Yes | Yes | Yes | Yes |
| Triggers | Yes | Yes | Yes | Yes |
| Functions | Yes | Yes | Yes | -- |
| Procedures | Yes | Yes | Yes | -- |
| Types | Yes | -- | Yes | -- |
| Locks | Yes | Yes | Yes | -- |
| Connections | Yes | Yes | Yes | -- |
SQLite does not support stored procedures, functions, or user-defined types, and has no server to report locks or connections for. MySQL does not support user-defined types. These categories return empty results for unsupported dialects.
Type support by dialect:
- PostgreSQL - Enums, composite types, domains
- MSSQL - Alias types (e.g.,
CREATE TYPE EmailAddress FROM VARCHAR(255)), table types
Common Use Cases
Verify builds. After running schema builds, use explore to confirm tables, views, and functions were created correctly.
Check constraints. Browse foreign keys to verify referential integrity is set up as expected before deploying.
Inspect indexes. Review index coverage before and after adding new queries to ensure performance.
Compare environments. Run overview on dev vs prod to spot schema drift quickly. Use different configs with the -c flag.
Debug changes. When changes fail, explore the current schema state to understand what exists.
What's Next?
- SQL Terminal - Run queries against your database
- Teardown - Reset database objects
- Configs - Connect to different databases