tools db browser - BevvyTech/BrewskiDocs GitHub Wiki
-
Dynamic Schema Introspection
- Never hardcode column names or relationships.
- Always read live schema data from Postgres using
information_schemaandpg_catalog. - Use this base query to get all visible columns:
SELECT table_name, column_name, data_type, is_nullable, column_default FROM information_schema.columns WHERE table_schema = 'public' ORDER BY table_name, ordinal_position;
- Cache results in memory to reduce load and refresh after migrations.
-
Foreign Key Discovery
- Discover foreign keys dynamically:
SELECT kcu.table_name AS source_table, kcu.column_name AS fk_column, ccu.table_name AS target_table, ccu.column_name AS target_column FROM information_schema.table_constraints AS tc JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name WHERE tc.constraint_type = 'FOREIGN KEY';
- Merge this into a mapping
{source_table.fk_column → {target_table, target_column}}.
- Discover foreign keys dynamically:
-
Friendly Columns
- Prefer columns named (case-insensitive):
name,reference,title,label,code,description - When resolving foreign keys, select one of those fields if available.
- If no such column exists, fall back to any text field shorter than 100 chars.
- Avoid IDs, UUIDs, booleans, timestamps, and numeric columns as labels.
- Prefer columns named (case-insensitive):
-
Metadata Table (optional but preferred)
- Use
table_labelsif it exists:CREATE TABLE IF NOT EXISTS table_labels ( table_name text PRIMARY KEY, label_columns text[] );
- Read mappings from there first; only use heuristics when missing.
- Use
-
Runtime Behaviour
- When rendering tables in the admin browser:
- Use live schema to build headers and data grids.
- Automatically join FK targets to display friendly names.
- Validate columns before running
$queryRaw()to avoid “column does not exist” errors.
- Never assume a fixed list of label columns — always compute dynamically.
- When rendering tables in the admin browser:
-
Schema Sync
- Regenerate or refresh schema cache whenever:
- a migration runs, or
-
prisma migrate deploycompletes.
- Store the cached schema as
src/config/schema-map.jsonfor offline operation.
- Regenerate or refresh schema cache whenever:
-
Fail Gracefully
- If a label column is missing, display the raw ID but log a warning.
- Never break API routes because of schema drift.
The DB browser must be entirely data-driven. Discover columns, types, and foreign keys dynamically. Use heuristics for “friendly” display fields. Do not hardcode schema details in code.
- Give super user team members a read-only view of production database tables for diagnostics and support triage.
- Consolidate table selection, column filtering, and pagination into a single interface so operators do not need direct database access.
- Navigation item, landing card, and page content appear only when the current membership has
teamRoleType === "su". - Non-super users attempting to load the page are redirected back to
/tools.
- Split layout: left column (~30% width on desktop) lists tables from the
publicschema along with row-count pills; right column houses the search controls and data preview. - Breadcrumb trail keeps context (
Dashboard → Tools → DB Browser), followed by a read-only notice. - Right column stacks two cards: search/filter form on top and a results table with pagination beneath.
- Selecting a table resets pagination and clears search filters before requesting rows via
dbBrowserService.fetchRows. - Search form requires both a column (populated from the latest metadata) and a term; submitting triggers a re-fetch, while Reset clears filters and reloads the first page.
- Results card exposes two tabs (“Rows”, “Columns”); the last selected tab persists in the
brewski_tools_db_browser_tabcookie for a year. - Rows tab renders an MUI table with sticky headers, displaying JSON-serialised values (nulls shown as
—).TablePaginationreflects backendtotal,page, andpageSize. - Every visible column header on the Rows tab wraps a
TableSortLabel. First click sorts ascending, second click on the same column flips to descending, and selecting a different column resets it to ascending. Each click re-fetches rows withsort=<column>anddirection=<asc|desc>so the backend does the ordering. - Recognised foreign keys (
team_id,client_id,user_id,beer_id,batch_id,brewer_id) surface the related entity name inline, with click-to-copy behaviour for the underlying ID and tooltips when the label differs. - Columns tab lists every column with size-aware type labels (e.g.,
numeric(10,2)) and a visibility checkbox. Toggling a column hides/shows it in the Rows tab and persists the choice per table/column via cookies (brewski_tools_db_browser_{table}_{column}_enabled). - Cell content truncates with ellipsis; clicking any row opens a dialog showing the full JSON payload for inspection.
- Row detail dialogs now show JSON on the left and, when references exist, a related-records card on the right with quick links to the matching Admin pages (users, teams, clients, beers, batches). IDs remain copy-to-clipboard.
- Columns tab lists every column name and data type returned by the API so operators can review schema context without leaving the tool.
- Loading states display a spinner and helper copy, and API errors surface through an inline
Alert. - A timestamp at the bottom reflects the most recent fetch using the active locale.
- The table footer includes
Sorted by {column} ({direction}), wheredirectionis a localized “ascending”/“descending” string that mirrors the server’s reported ordering.
-
dbBrowserService.listTables→GET /tools/db/tablesto populate the left-hand list. -
dbBrowserService.fetchRows→GET /tools/db/tables/:table/rows(withpage,pageSize, optionalsearchColumn/searchTerm, plussort/direction) to retrieve column metadata and row data. - Uses the global auth token; relies on existing unauthorised handler to broadcast
brewski:unauthorizedwhen a 401 is returned.