A management UI and REST API gateway for Cloudflare D1 β bringing your edge database to the outside world, built with Vue 3 + Naive UI + TypeScript + Vite.
Cloudflare D1 is built to live inside the Cloudflare ecosystem. Data flows in and out through Worker bindings and platform-native APIs β which works perfectly for edge applications, but leaves a gap: accessing D1 from the outside world requires writing custom Worker code for every use case.
D1 SQL Studio bridges that gap with a single, self-hosted Worker that exposes your database as a standardized REST API β authenticated, paginated, and fully queryable β so any external system (scripts, dashboards, data pipelines, BI tools) can interact with D1 in a consistent, predictable way. The management UI is the human-friendly layer on top of that same API.
- π¨ Professional UI - Built with Vue 3 + Naive UI for a tooling-grade experience
- π Table Management - Create, view, and delete tables with a visual builder and SQL editor
- π Data Browser - Browse and manage table data with pagination, sorting, and search
- β‘ SQL Query Editor - Execute custom SQL with CodeMirror 6, syntax highlighting, and autocompletion
- ποΈ Query History - Persistent query history with search and filter
- π Secure Authentication - Multi-user API key management with KV storage
- π― Visual Table Builder - Build table schemas with a GUI β real-time SQL preview
- π API Key Management - Generate, manage, and revoke API keys through the UI
- π‘οΈ Security Hardened - SQL injection protection and identifier validation
- π Edge Computing - Runs on Cloudflare Workers for global performance
- π REST API - Complete REST API for programmatic access
- π Multi-Field Sorting - Sort data by multiple columns simultaneously
- π Multi-Table Joins - Structured JOIN queries with RESTful API
- Runtime: Cloudflare Workers
- Database: Cloudflare D1 (SQLite-based)
- API Keys Storage: Cloudflare KV
- Static Assets: Workers Assets
- Location:
src/worker/
- Framework: Vue 3 + Composition API
- UI Library: Naive UI
- State Management: Pinia
- Routing: Vue Router
- Editor: CodeMirror 6
- Language: TypeScript
- Bundler: Vite
- Location:
src/ui/
βββββββββββββββββββ
β Cloudflare β
β Workers Edge β
βββββββββββββββββββ€
β Static Assets β β Vite build output
β (index.html) β
βββββββββββββββββββ€
β API Routes β β REST API (/api/*)
β (/api/*) β
βββββββββββββββββββ€
β D1 Database β β SQLite (Data)
βββββββββββββββββββ€
β KV Storage β β API Keys
βββββββββββββββββββ
npm installStart the frontend development server (with HMR):
npm run devAccess at http://localhost:5173
Start the Worker development server (in a separate terminal):
npm run dev:workerWorker runs at http://localhost:8787
The frontend proxies /api/* requests to the Worker automatically.
Option 1: Configure in wrangler.toml (Local Development)
- Create D1 database:
npx wrangler d1 create d1-sql-studio-db- Copy the
database_idfrom the output and updatewrangler.toml:
[[d1_databases]]
binding = "DB"
database_name = "d1-sql-studio-db"
database_id = "your-database-id-here"- Restart the dev server
Option 2: Cloudflare Dashboard (Production, Recommended)
- Deploy your Worker
- Go to Cloudflare Dashboard β Workers & Pages β Your Worker β Settings β Bindings
- Add D1 binding: Variable name =
DB, select/create your database - Save and wait a few seconds
API keys are managed through the UI and stored in Cloudflare KV.
First-time setup:
- Deploy the app (see below)
- Visit your app URL β you'll see the first-time setup screen
- Create your first API key
- Save the generated key β it's only shown once
- Log in with your new key
# Build frontend only
npm run build:ui
# Type-check worker
npm run build:worker
# Full build (frontend + type check)
npm run buildnpx wrangler d1 create d1-sql-studioCopy the database_id and update wrangler.toml:
[[d1_databases]]
binding = "DB"
database_name = "d1-sql-studio"
database_id = "your-database-id-here"npx wrangler kv:namespace create "API_KEYS"Copy the id and update wrangler.toml:
[[kv_namespaces]]
binding = "API_KEYS"
id = "your-kv-id-here"npm run deployThis builds the frontend and deploys to Cloudflare Workers.
- Visit your Worker URL
- See the First-Time Setup screen
- Enter a name for your first API key and click Generate API Key
- Copy and save the key securely β it won't be shown again
- Click Continue to Dashboard
d1-sql-studio/
βββ src/
β βββ worker/ # Backend (Cloudflare Worker)
β β βββ index.ts # Worker entry point
β β βββ router.ts # API routing
β β βββ db.ts # D1 database operations
β β βββ auth.ts # Authentication
β β βββ apikeys.ts # API key management (KV)
β β βββ security.ts # Security validation
β β βββ types.ts # TypeScript types
β βββ ui/ # Frontend (Vue 3 SPA)
β βββ main.ts # App entry point
β βββ App.vue # Root component (Naive UI providers)
β βββ env.d.ts # Vue module declarations
β βββ router/
β β βββ index.ts # Vue Router + navigation guards
β βββ stores/
β β βββ auth.ts # Auth state (apiKey, login/logout)
β β βββ tables.ts # Table list + selected table
β β βββ notification.ts # showToast / showConfirm
β βββ composables/
β β βββ useTableSchema.ts # Schema loading with 5-min cache
β β βββ useExport.ts # CSV / JSON / SQL export
β βββ layouts/
β β βββ DashboardLayout.vue # Sidebar + main content layout
β βββ views/
β β βββ auth/
β β β βββ LoginView.vue
β β β βββ FirstTimeSetupView.vue
β β βββ tables/TablesView.vue
β β βββ data/DataBrowserView.vue
β β βββ query/QueryEditorView.vue
β β βββ keys/ApiKeysView.vue
β βββ components/
β β βββ tables/
β β β βββ CreateTableModal.vue
β β β βββ VisualTableBuilder.vue # GUI builder + SQL preview
β β β βββ EditTableModal.vue # Columns / Rename / Indexes tabs
β β βββ data-browser/
β β β βββ AddRowModal.vue
β β β βββ EditRowModal.vue
β β β βββ ApiDocumentation.vue # Collapsible REST API reference
β β βββ query-editor/
β β β βββ SqlEditor.vue # CodeMirror 6 wrapper
β β β βββ ResultsTable.vue
β β β βββ QueryHistory.vue
β β β βββ KeyboardShortcuts.vue
β β βββ shared/
β β βββ NullValue.vue
β βββ lib/ # Pure TypeScript utilities (no framework deps)
β βββ api.ts # ApiClient class
β βββ exportUtils.ts # CSV / JSON / SQL export
β βββ queryHistory.ts # localStorage query history
β βββ utils.ts
βββ public/ # Static assets
βββ dist/ # Build output
βββ index.html # HTML template
βββ vite.config.ts # Vite configuration
βββ tsconfig.json # TypeScript config (UI)
βββ tsconfig.worker.json # TypeScript config (Worker)
βββ wrangler.toml # Cloudflare Workers config
βββ package.json
All API endpoints require the Authorization header:
Authorization: Bearer your-api-keyGET /api/tablesGET /api/tables/:tableName/schemaGET /api/tables/:tableName/rows?page=1&limit=50&sortBy=id&sortOrder=asc&search=keywordQuery Parameters:
pageβ Page number (default: 1)limitβ Rows per page (default: 50, max: 1000)sortByβ Column to sort by (single field)sortOrderβascordesc(default: asc)sortβ Multi-field sort, format:field1:order1,field2:order2searchβ Keyword search across all TEXT columns
Multi-field sort examples:
# Sort by name ascending, then created_at descending
GET /api/tables/users/rows?sort=name:asc,created_at:descPOST /api/query
Content-Type: application/json
{
"sql": "SELECT * FROM users WHERE created_at > ? LIMIT ?",
"params": ["2024-01-01", 10]
}Allowed statements: SELECT, INSERT, UPDATE, DELETE, PRAGMA
Security: parameterized queries, multiple statements blocked, DDL not allowed via this endpoint.
POST /api/join
Content-Type: application/json
{
"baseTable": "users",
"joins": [
{ "table": "orders", "type": "LEFT", "on": "users.id = orders.user_id" }
],
"select": ["users.*", "COUNT(orders.id) as order_count"],
"where": "users.created_at > ?",
"groupBy": ["users.id"],
"orderBy": "order_count DESC",
"limit": 20,
"params": ["2024-01-01"]
}| Parameter | Type | Required | Description |
|---|---|---|---|
baseTable |
string | β | Base table name |
joins |
JoinConfig[] | β | JOIN configurations (1β10 joins) |
select |
string[] | β | Columns to select (default: ["*"]) |
where |
string | β | WHERE clause with ? placeholders |
groupBy |
string[] | β | GROUP BY columns |
having |
string | β | HAVING clause |
orderBy |
string | β | ORDER BY clause |
limit |
number | β | Max records (max: 1000) |
offset |
number | β | Pagination offset |
params |
any[] | β | Values for WHERE/HAVING |
POST /api/tables # Create table (body: { sql })
DELETE /api/tables/:tableName # Drop table
POST /api/tables/:tableName/rows # Insert row
PUT /api/tables/:tableName/rows/:id # Update row
DELETE /api/tables/:tableName/rows/:id # Delete rowPOST /api/tables/:tableName/columns/:columnName # Add column
PUT /api/tables/:tableName/columns/:columnName # Rename column
DELETE /api/tables/:tableName/columns/:columnName # Drop column
PUT /api/tables/:tableName/rename # Rename tableGET /api/tables/:tableName/indexes # List indexes
GET /api/tables/:tableName/indexes/:indexName/columns # Index columns
POST /api/tables/:tableName/indexes # Create index
DELETE /api/tables/:tableName/indexes/:indexName # Drop indexGET /api/keys # List keys
POST /api/keys # Create key (body: { name, description? })
DELETE /api/keys/:id # Delete key
GET /api/keys/status # Check if any keys exist (unauthenticated)| Command | Description |
|---|---|
npm run dev |
Start frontend dev server (port 5173) |
npm run dev:worker |
Start Worker dev server (port 8787) |
npm run build |
Full build (UI + type check) |
npm run build:ui |
Build frontend only |
npm run build:worker |
Type-check Worker |
npm run preview |
Preview production build |
npm run deploy |
Build UI + deploy to Cloudflare |
Frontend:
- π’ Vue 3 β Composition API
- π¨ Naive UI β Professional component library
- π Pinia β State management
- π Vue Router β Client-side routing
- π CodeMirror 6 β SQL editor with syntax highlighting
- π TypeScript 5
- β‘ Vite 5
Backend:
- βοΈ Cloudflare Workers β Edge computing
- ποΈ Cloudflare D1 β SQLite database
- π¦ Workers Assets β Static asset serving
Tools:
- π¨ Wrangler β Cloudflare CLI
- π¦ npm
- API Key Management β Multi-user support, bcrypt-hashed storage in KV
- SQL Injection Protection β Identifier validation with strict regex
- Query Whitelisting β Only SELECT/INSERT/UPDATE/DELETE/PRAGMA via
/api/query - Identifier Quoting β All SQL identifiers automatically quoted
- Same-Origin CORS β API restricted to same-origin requests by default
- Session Persistence β Auto-login from localStorage, cleared on logout
- Create D1 database:
npx wrangler d1 create d1-sql-studio - Update
database_idinwrangler.toml - Redeploy:
npm run deploy
- Create KV namespace:
npx wrangler kv:namespace create "API_KEYS" - Update
wrangler.tomlwith namespace ID - Redeploy and clear browser cache
rm -rf node_modules package-lock.json
npm install
npm run buildtsc --noEmit # Frontend
tsc --project tsconfig.worker.json # WorkerContributions are welcome! Please feel free to submit a Pull Request.
MIT License β see LICENSE for details.
- Built for Cloudflare Workers
- Powered by Cloudflare D1
- UI with Vue 3 + Naive UI
- Editor by CodeMirror
- Bundled by Vite
Star β this repo if you find it useful!