Skip to main content
Alvin QuachFull Stack Developer
HomeProjectsExperienceBlog
HomeProjectsExperienceBlog
alvinquach

Full Stack Developer building systems that respect complexity.

Open to opportunities

AQ

Projects

  • All Projects
  • Hoparc Physical Therapy
  • OpportunIQ
  • Hoop Almanac
  • SculptQL

Knowledge

  • Blog
  • Experience
  • Interview Prep

Connect

  • Contact
  • LinkedIn
  • GitHub
  • X

Resources

  • Resume
© 2026All rights reserved.
Back to Blogs
Build Log
Featured
Depth: ●●●○○

Building SculptQL: A Local-First SQL IDE with CodeMirror

How I built SculptQL, a local-first SQL IDE with zero-latency querying. Features CodeMirror for SQL autocomplete, D3.js for schema visualization, and keeps database credentials secure by never leaving your machine.

Published January 24, 20264 min readImportance: ★★★★★
Share:
schema-introspection.ts
typescript
// Server action for schema introspection
'use server';

import { z } from 'zod';

const ConnectionSchema = z.object({
  type: z.enum(['postgres', 'mysql', 'sqlite', 'sqlserver', 'oracle']),
  connectionString: z.string().min(1),
});

export async function introspectSchema(formData: FormData) {
  const parsed = ConnectionSchema.safeParse({
    type: formData.get('type'),
    connectionString: formData.get('connectionString'),
  });

  if (!parsed.success) {
    return { error: 'Invalid connection details' };
  }

  // Connect and introspect - runs on server, credentials never leave
  const { type, connectionString } = parsed.data;
  const schema = await getSchemaFromDatabase(type, connectionString);
  
  return { schema };
}

SculptQL started as a frustration. I was constantly switching between database GUIs, writing raw SQL in text editors, and trying to remember schema structures. I wanted a local-first SQL IDE that would let me query any database with zero latency while keeping my credentials completely secure. So I built it.

The Problem

SQL tools fall into two camps: cloud-hosted (convenient but your credentials leave your machine) or heavyweight desktop apps (secure but clunky). I wanted something in between: a modern web-based IDE that runs entirely locally, connects directly to databases, and never phones home.

I wanted a tool that would: 1) Support multiple database types (Postgres, MySQL, SQLite, SQLServer, Oracle), 2) Visualize schema relationships with an ERD, 3) Provide intelligent SQL autocomplete, 4) Run entirely client-side for security, 5) Feel like a real IDE.

Architecture: Local-First Design

SculptQL is built with Next.js, TypeScript, and CodeMirror. The key architectural decision was local-first: database connections happen through server actions that run on the same machine. Your credentials never traverse the network. The architecture has three main parts: Schema Introspection (connects and discovers tables/columns), ERD Visualization (D3.js-powered schema diagrams), and Query Editor (CodeMirror with SQL language support).

Schema Introspection via Server Actions

When you connect to a database, a server action runs locally to introspect the schema. This returns all tables, columns, types, foreign keys, and indexes. I parse this into a tree structure that powers both the ERD visualization and CodeMirror's autocomplete. Using server actions instead of client-side useEffect means credentials stay on the server.

ERD Visualization with D3.js

The schema is rendered as an interactive Entity-Relationship Diagram using D3.js. Tables are draggable nodes showing columns and types. Foreign key relationships are drawn as connecting lines. You can zoom, pan, and click tables to see details. This makes understanding complex schemas intuitive.

CodeMirror Integration

CodeMirror 6 powers the query editor. I configured it with SQL syntax highlighting, autocomplete for table and column names (populated from the introspected schema), and keyboard shortcuts matching VS Code. The editor supports multiple database SQL dialects.

Technical Challenges

Multi-Database Support

Each database has different introspection queries. Postgres uses information_schema, MySQL has SHOW commands, SQLite has sqlite_master. I built an adapter pattern where each database type implements a common interface: connect(), introspect(), query(), and disconnect().

IndexedDB for Persistence

Query history, saved queries, and schema metadata are stored in IndexedDB. This means your work persists across sessions without any server storage. Combined with the local-first architecture, SculptQL works completely offline once loaded.

Performance with Large Schemas

Production databases can have hundreds of tables. Rendering all of them in the ERD at once would freeze the UI. I implemented virtualization for the D3 canvas and lazy-loaded table details. The ERD shows a zoomed-out overview, expanding details only when you zoom in.

Tech Stack

Next.js 14 with TypeScript and Server Actions. CodeMirror 6 for the query editor. D3.js for ERD visualization. Zustand for client state management. IndexedDB for persistence. Tailwind CSS for styling. The app is fully typed end-to-end.

Lessons Learned

1. Server actions are powerful. Moving database connections to server actions meant zero client-side credential handling. The security model is simple: credentials exist only in server memory during the request.

2. D3.js force layouts need tuning. The default force simulation parameters made the ERD jumpy. I spent time tuning link distance, charge strength, and collision detection to make the layout stable and readable.

3. CodeMirror 6 is worth learning. The new architecture is different from v5, but the extension system is incredibly flexible. Building custom autocomplete completers was straightforward once I understood the pattern.

What's Next

I'm working on: Query explain/analyze visualization, export results to CSV/JSON, saved connection profiles (encrypted), and AI-powered query suggestions. The local-first foundation is solid—now it's about making SQL workflows even smoother.