A Prisma Studio-like visual database browser and editor for Go applications using GORM. Browse schemas, manage data, run SQL, export ERD diagrams, import data, and generate Go models — all from a single studio.Mount() call.
✨ Features
- Schema Discovery — Introspects your database AND parses GORM model structs via reflection
- Browse & Filter — Paginated data grid with column sorting and full-text search
- CRUD Operations — Create, edit, and delete records through modal forms
- Relationship Navigation — See and navigate foreign key relationships (has_one, has_many, belongs_to, many_to_many)
- Raw SQL Editor — Execute SQL queries with automatic read/write detection and DDL blocking
- Bulk Operations — Select multiple rows for batch deletion
- Schema Export — Export as SQL DDL, JSON, YAML, DBML, PNG ERD diagram, or PDF ERD diagram
- Data Export — Export entire database as JSON, CSV (ZIP), or SQL INSERT statements
- Data Import — Import data from JSON, CSV, SQL, or Excel (.xlsx) files
- Schema Import — Import schemas from SQL, JSON, YAML, or DBML files to create tables
- Go Code Generation — Generate Go model structs from your database schema with proper GORM tags
- Go Models Import — Upload a
.gofile with struct definitions to create database tables - Custom Login UI — Beautiful themed login page replaces the browser's native auth popup
- Confirm Delete Modal — Styled confirmation dialog for record deletion (replaces browser
confirm()) - Authentication — Built-in
AuthMiddlewaresupport for protecting routes - Security — DDL blocking, SQL injection prevention, CSV formula injection protection, SRI hashes
- Zero Config — Just pass your
*gorm.DBand model list — one line to mount
📖 Documentation
Full documentation is available at the GORM Studio Docs Site.
To run the docs locally:
cd docs-site
npm install
npm run dev🚀 Quick Start
1. Install
# Create a new Project go mod init github.com/yourusername/your-project # Install GORM Studio go get github.com/MUKE-coder/gorm-studio/studio
Install Other Packages
go get github.com/gin-gonic/gin go get gorm.io/gorm go get github.com/glebarez/sqlite # for SQLite (pure Go, no CGO) go get gorm.io/driver/postgres # for PostgreSQL go get gorm.io/driver/mysql # for MySQL
2A. Mount in your Gin app USING SQLITE
package main import ( "log" "net/http" "github.com/MUKE-coder/gorm-studio/studio" "github.com/gin-gonic/gin" "github.com/glebarez/sqlite" "gorm.io/gorm" ) type User struct { ID uint `gorm:"primarykey" json:"id"` Name string `gorm:"size:100" json:"name" binding:"required"` Email string `gorm:"size:200;uniqueIndex" json:"email" binding:"required,email"` } func main() { db, err := gorm.Open(sqlite.Open("app.db"), &gorm.Config{}) if err != nil { log.Fatal("Failed to connect to database:", err) } db.AutoMigrate(&User{}) router := gin.Default() // POST /users - Create a new user router.POST("/users", func(c *gin.Context) { var user User if err := c.ShouldBindJSON(&user); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if result := db.Create(&user); result.Error != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": result.Error.Error()}) return } c.JSON(http.StatusCreated, gin.H{"data": user}) }) // Mount GORM Studio — that's it! studio.Mount(router, db, []interface{}{&User{}}) router.Run(":8080") }
2B. Mount in your Gin app USING PostgreSQL
ENV FILE
PGHOST='' PGDATABASE='neondb' PGUSER='neondb_owner' PGPASSWORD='' PGSSLMODE='require' PGCHANNELBINDING='require'
MAIN FILE
package main import ( "fmt" "log" "os" "github.com/MUKE-coder/gorm-studio/studio" "github.com/joho/godotenv" "github.com/gin-gonic/gin" "gorm.io/driver/postgres" "gorm.io/gorm" ) type User struct { ID uint `gorm:"primarykey"` Name string `gorm:"size:100"` Email string `gorm:"size:200;uniqueIndex"` } func main() { err := godotenv.Load() if err != nil { log.Fatal("Error loading .env file:", err) } dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s sslmode=%s channel_binding=%s", os.Getenv("PGHOST"), os.Getenv("PGUSER"), os.Getenv("PGPASSWORD"), os.Getenv("PGDATABASE"), os.Getenv("PGSSLMODE"), os.Getenv("PGCHANNELBINDING"), ) db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{}) if err != nil { log.Fatal("Failed to connect to database:", err) } db.AutoMigrate(&User{}) router := gin.Default() // Mount GORM Studio — that's it! studio.Mount(router, db, []interface{}{&User{}}) router.Run(":8080") }
3. Open in browser
http://localhost:8080/studio
🎛️ Configuration
studio.Mount(router, db, models, studio.Config{ Prefix: "/studio", // URL prefix (default: "/studio") ReadOnly: false, // Disable write operations DisableSQL: false, // Disable raw SQL editor CORSAllowOrigins: []string{}, // Allowed CORS origins AuthMiddleware: nil, // Authentication middleware })
Authentication
When AuthMiddleware is configured, GORM Studio shows a custom login page instead of the browser's native auth popup. The React UI handles authentication gracefully — on 401, users see a themed login form with username/password fields. Credentials are stored in session storage for the duration of the browser session.
// Protect with basic auth — users see a custom login page studio.Mount(router, db, models, studio.Config{ AuthMiddleware: gin.BasicAuth(gin.Accounts{ "admin": "secret-password", }), }) // Or with JWT / custom middleware studio.Mount(router, db, models, studio.Config{ AuthMiddleware: func(c *gin.Context) { token := c.GetHeader("Authorization") if token == "" { c.AbortWithStatusJSON(401, gin.H{"error": "unauthorized"}) return } // Validate token... c.Next() }, })
Production Configuration
studio.Mount(router, db, models, studio.Config{ Prefix: "/admin/studio", ReadOnly: true, DisableSQL: true, AuthMiddleware: authMiddleware, CORSAllowOrigins: []string{"https://myapp.com"}, })
📁 Project Structure
studio/
├── studio.go # Mount function — registers routes on Gin
├── schema.go # Schema introspection (DB + GORM reflection)
├── handlers.go # REST API handlers (CRUD, filtering, SQL, per-table export)
├── frontend.go # Embedded React SPA (served as HTML string)
├── codegen.go # Go struct code generation from schema
├── sql_parser.go # CREATE TABLE SQL parser (SQLite/Postgres/MySQL)
├── erd.go # ERD diagram renderer (PNG + PDF)
├── export_schema.go # Schema export (SQL, JSON, YAML, DBML, PNG, PDF)
├── export_data.go # Full database data export (JSON, CSV ZIP, SQL)
├── import_schema.go # Schema import (SQL, JSON, YAML, DBML)
├── import_data.go # Data import (JSON, CSV, SQL, Excel)
└── import_models.go # Go model import (parse structs, create tables)
🔌 API Endpoints
Schema & Config
| Method | Endpoint | Description |
|---|---|---|
GET |
/studio |
Web UI |
GET |
/studio/api/schema |
Get full database schema |
POST |
/studio/api/schema/refresh |
Re-introspect schema |
GET |
/studio/api/config |
Get current config |
GET |
/studio/api/stats |
DB connection pool stats |
CRUD Operations
| Method | Endpoint | Description |
|---|---|---|
GET |
/studio/api/tables/:table/rows |
List rows (paginated, filterable) |
GET |
/studio/api/tables/:table/rows/:id |
Get single row |
POST |
/studio/api/tables/:table/rows |
Create row |
PUT |
/studio/api/tables/:table/rows/:id |
Update row |
DELETE |
/studio/api/tables/:table/rows/:id |
Delete row |
POST |
/studio/api/tables/:table/rows/bulk-delete |
Bulk delete |
GET |
/studio/api/tables/:table/rows/:id/relations/:rel |
Get related rows |
Export
| Method | Endpoint | Description |
|---|---|---|
GET |
/studio/api/export/schema?format=<fmt> |
Export schema (sql/json/yaml/dbml/png/pdf) |
GET |
/studio/api/export/data?format=<fmt> |
Export all data (json/csv/sql) |
GET |
/studio/api/export/models |
Download generated Go structs |
GET |
/studio/api/tables/:table/export?format= |
Export single table (json/csv) |
Import
| Method | Endpoint | Description |
|---|---|---|
POST |
/studio/api/import/schema |
Import schema (.sql/.json/.yaml/.dbml) |
POST |
/studio/api/import/data |
Import data (.json/.csv/.sql/.xlsx) |
POST |
/studio/api/import/models |
Import Go structs (.go) |
SQL
| Method | Endpoint | Description |
|---|---|---|
POST |
/studio/api/sql |
Execute raw SQL |
Query Parameters for listing rows
page— Page number (default: 1)page_size— Rows per page (default: 50, max: 500)sort_by— Column to sort bysort_order—ascordescsearch— Full-text search across all text columnsfilter_<column>— Filter by column value (use%for LIKE)show_deleted— Include soft-deleted rows (default: false)
🗃️ Supported Databases
- ✅ SQLite (via
github.com/glebarez/sqlite— pure Go, no CGO required) - ✅ PostgreSQL (via
gorm.io/driver/postgres) - ✅ MySQL (via
gorm.io/driver/mysql)
📤 Export Features
Schema Export
Export your database schema in multiple formats:
- SQL — CREATE TABLE DDL statements
- JSON — Structured schema definition
- YAML — Human-readable schema format
- DBML — Database Markup Language (compatible with dbdiagram.io)
- PNG — Entity Relationship Diagram as image
- PDF — Entity Relationship Diagram as PDF
Data Export
Export your entire database:
- JSON — Single document with all tables and rows
- CSV — ZIP archive with one CSV per table (formula injection protected)
- SQL — INSERT statements for all data
Go Models Export
Generate a downloadable .go file with struct definitions for all tables, including proper GORM tags, type mapping, and relationship fields.
📥 Import Features
Schema Import
Upload a schema file to create tables:
.sql— CREATE TABLE statements (auto-detects SQLite/PostgreSQL/MySQL dialect).json— Structured table definitions.yaml— YAML table definitions.dbml— DBML format
Returns created table names and generated Go model code.
Data Import
Upload data files into existing tables:
.json— Multi-table or single-table format.csv— Requirestablequery parameter.sql— Only INSERT statements processed (safe).xlsx— Excel files, requirestablequery parameter
Go Models Import
Upload a .go file with struct definitions to automatically create database tables. Parses field types, GORM tags, and creates corresponding columns.
🔒 Security
GORM Studio includes multiple security layers:
- Authentication — Built-in
AuthMiddlewaresupport with custom login UI and startup warnings when unprotected - Table Validation — All table names validated against registered models
- Column Validation — Only known columns accepted for filtering and sorting
- Parameterized Queries — Uses GORM's built-in query parameterization
- Identifier Quoting — Dialect-specific quoting (double quotes for SQLite/Postgres, backticks for MySQL)
- DDL Blocking — DROP, ALTER, TRUNCATE, CREATE, ATTACH, DETACH, GRANT, REVOKE always blocked in SQL editor
- CSV Formula Injection — Cells starting with
=,+,-,@are prefixed with' - SRI Hashes — CDN scripts include Subresource Integrity hashes
Production Recommendations
- Always add
AuthMiddlewarein non-development environments - Use
ReadOnly: trueto prevent accidental mutations - Use
DisableSQL: trueto prevent arbitrary SQL execution - Restrict access via network policies
- Use a database user with minimal required permissions
🏗️ Running the Demo
cd gorm-studio
go mod tidy
go run main.goThen open http://localhost:8080/studio — you'll see a demo blog database with Users, Posts, Comments, Tags, and Profiles.
📝 Notes
- The SQL editor allows both read and write queries (write can be disabled with
ReadOnly: true) - Set
DisableSQL: trueto hide the SQL editor entirely - Schema is cached at startup; use the refresh button or
POST /api/schema/refreshto re-introspect - Import endpoints are only available when
ReadOnlyis false - Soft-deleted rows (GORM
DeletedAt) are hidden by default — useshow_deleted=trueto include them
🤝 Contributing
Contributions are welcome! Please see the contributing guide for details.
📄 License
MIT
👨💻 Author
Built with ❤️ by JB