The Problem
Developers need a single UI that can connect to any SQL/NoSQL database without the overhead of installing a heavyweight tool like pgAdmin. Switching between Postgres, MySQL, MongoDB, Redis, or SQLite requires separate clients, each with its own quirks, which slows debugging and schema exploration.
What This Does
db-studio is a self‑hosted, cross‑database management UI built on React 19 (frontend) and a Hono‑based Node server (backend). The repository is a monorepo split into three logical packages:
- packages/server – the API layer that talks to the selected database driver (
mongo.adapter.ts,pg.adapter.ts, etc.) and exposes REST endpoints (routes/*.routes.ts). - packages/ui – the React component library (
primitives/button.tsx,utils.ts) that renders tables, query editors, and connection dialogs. - packages/proxy – a Cloudflare‑Workers proxy (
src/index.ts) used when the app is deployed behind a CDN.
The UI imports shared type definitions from packages/shared/src/types/database.types.ts (13 dependents) and utility helpers from packages/ui/src/utils.ts (35 dependents), making those modules the highest‑impact “hub” points in the codebase.
How It Is Wired
- CLI entry –
npx db-studioinvokes the binary defined in the rootpackage.json. That binary loadspackages/server/src/cmd/args.ts, parses CLI flags (--env,--database-url, etc.), and then callspackages/server/src/cmd/load-env.tsto locate a.envfile. - Server bootstrap –
packages/server/src/index.tscreates a Hono app viautils/create-server.ts. Middlewaremiddlewares/error-handler.tsis attached, then each route file (routes/*.routes.ts) registers its endpoints. - Database adapters – Each route handler delegates to an adapter (
src/adapters/*/*.adapter.ts). For example,routes/tables.routes.tsultimately callspackages/server/src/dao/mongo/table-list.mongo.dao.ts, which usesmongo.utils.tsfor connection handling. The adapters share thepackages/shared/src/types/*definitions. - Proxy (optional) – When deployed to Cloudflare,
packages/proxy/src/index.tsspins up a worker that forwards API calls to the server, applying rate limits fromshared/constants/proxy-limits.ts. - Frontend startup – The UI bundle is produced by Vite (see
package.jsonscripts). The entry pointpackages/ui/src/index.tsre‑exports components; the router (react-router) loads feature modules such asfeatures/tables/components/table-header.tsx. The most connected UI file ispackages/ui/src/utils.ts, which is imported by 35 other UI modules, so any change there ripples widely.
Circular import cycles appear in the query‑runner UI (runner-screen.tsx ↔ runner-header.tsx ↔ www/src/routeTree.gen.ts), increasing the risk of runtime undefined errors if a module’s export shape changes.
How To Use It
# Clone the repo
git clone https://github.com/moses-y/db-studio
cd db-studio
# Install dependencies with Bun (preferred) or npm
bun install # reads bun.lock & bunfig.toml
# or npm ci # note: no lockfile is present, builds are non‑reproducible
# Create a .env in the project root (or any parent) with:
# DATABASE_URL=postgresql://user:pass@host:5432/dbname
# Run the UI locally
npx db-studio # uses default port 3333
# Or override flags:
npx db-studio --port 4000 --env .env.local
The server starts on the chosen port, reads the connection string, and serves the React UI at http://localhost:<port>. No additional build step is required because Vite runs in dev mode when launched via the binary.
Real‑World Use
A SaaS platform can embed db-studio in its admin console to let support engineers inspect a customer's PostgreSQL or MongoDB instance without leaving the internal dashboard. The platform would set DATABASE_URL dynamically per tenant, start the binary as a child process, and proxy the UI through the same domain, relying on the shared type definitions to keep client‑side and server‑side schemas consistent.
Code Health & Issues
Measured findings (static analysis)
- HIGH – Hub module:
packages/ui/src/utils.ts,packages/shared/src/types/database.types.ts,packages/ui/src/primitives/button.tsxeach have >30 dependents → high blast radius. - HIGH – Deep nesting: 49 files (e.g.,
packages/web/src/shared/api/client.ts) contain indentation depth ≥ 8, making logic hard to follow. - MEDIUM – High branching density:
packages/server/src/dao/mongo/mongo.utils.tshas 52 branches in 168 lines. - HIGH – Duplicated code: 1 365 repeated 6‑line blocks across 146 files (adapter layer).
- HIGH – Import cycle:
packages/web/src/features/query-runner/screens/runner-screen.tsx↔runner-header.tsx↔www/src/routeTree.gen.ts. - HIGH – Oversized file:
packages/web/src/features/tables/components/table-cell-variant.tsx(1 204 lines). - MEDIUM – Empty catch:
packages/web/src/routes/__root.tsxsilently discards errors.
Code‑health audit
- HIGH – Pin GitHub Action versions to commit SHAs (
.github/workflows/*). - MEDIUM – Declare least‑privilege
GITHUB_TOKENpermissions (.github/workflows/check.yml). - MEDIUM – Add a dependency‑vulnerability scan to CI.
- LOW – Set
timeout-minuteson workflow jobs. - LOW – Add missing convention files (
.editorconfig,.gitattributes, formatter config).
Additional observations: the repo lacks a lockfile, so builds are non‑reproducible; tests exist (51 files) and CI runs via GitHub Actions; no Dockerfile is provided, so containerisation must be added manually if required.
The Bottom Line
db-studio delivers a functional, single‑pane UI for many databases with a clear separation of concerns between adapters, shared types, and the React front‑end. The codebase is actively tested but suffers from high‑impact hub modules, duplicated adapter logic, and a few architectural smells (deep nesting, import cycles). It is suitable for teams that need a quick, extensible admin UI and are comfortable addressing the identified hotspots before scaling in production.