Schemaflow is a visual schema editor for designing relational data models on an interactive canvas.
You can:
- Create and arrange models visually
- Add/edit fields and data types
- Define relationships between fields
- Work with multiple views (tabs)
- Import JSON into the current view
- Import SQL schema dumps (
CREATE TABLE ...) - Export the current view as JSON or SQL (PostgreSQL/MySQL/SQLite)
- Export multiple views as JSON
- Share selected views with a compressed URL
Requirements:
- Node.js 20+ (recommended)
- npm
Install and run:
npm install
npm run devOpen http://localhost:3000.
npm run dev
npm run build
npm run start
npm run lintSchemaflow supports hardcoded sample sessions via URL query params.
- Single sample:
/?sample=audiobookshelf - Multiple samples (comma-separated):
/?sample=audiobookshelf,grimmory - Multiple samples (repeated params):
/?sample=audiobookshelf&sample=storyteller - Minified books-focused sample:
/?sample=abs-min - Multiple minified samples:
/?sample=abs-min,grimmory-min,storyteller-min
When a sample URL is loaded:
- The current saved session is cleared
- Sample views are loaded as tabs
- The URL is cleaned back to
/
Available keys include:
- Full:
audiobookshelf,grimmory,storyteller - Minified (books + metadata/progress focused):
abs-min(alias ofaudiobookshelf-min),audiobookshelf-min,grimmory-min,storyteller-min
Sample mappings are defined in lib/sample-sessions.ts, and JSON payloads live in public/samples.
Schemaflow currently uses two JSON shapes depending on action:
- Import into current view: expects a single
Schemaobject. - Export/share selected views: returns an array of
{ name, schema }.
Use this in the Import dialog to replace the active view:
{
"models": [
{
"id": "user",
"name": "User",
"position": { "x": 120, "y": 80 },
"fields": [
{ "id": "user-id", "name": "id", "type": "uuid", "primaryKey": true },
{ "id": "user-email", "name": "email", "type": "string", "unique": true },
{ "id": "user-name", "name": "name", "type": "string", "nullable": true }
]
},
{
"id": "post",
"name": "Post",
"position": { "x": 520, "y": 80 },
"fields": [
{ "id": "post-id", "name": "id", "type": "uuid", "primaryKey": true },
{ "id": "post-title", "name": "title", "type": "string" },
{ "id": "post-user-id", "name": "userId", "type": "uuid", "foreignKey": true }
]
}
],
"relationships": [
{
"id": "rel-post-user",
"fromModelId": "post",
"fromFieldId": "post-user-id",
"toModelId": "user",
"toFieldId": "user-id",
"type": "many-to-one"
}
]
}This is the shape used when exporting selected views:
[
{
"name": "Core",
"schema": {
"models": [
{
"id": "user",
"name": "User",
"position": { "x": 120, "y": 80 },
"fields": [
{ "id": "user-id", "name": "id", "type": "uuid", "primaryKey": true }
]
}
],
"relationships": []
}
},
{
"name": "Billing",
"schema": {
"models": [
{
"id": "invoice",
"name": "Invoice",
"position": { "x": 160, "y": 120 },
"fields": [
{ "id": "invoice-id", "name": "id", "type": "uuid", "primaryKey": true },
{ "id": "invoice-user-id", "name": "userId", "type": "uuid", "foreignKey": true }
]
}
],
"relationships": []
}
}
]- Session state is persisted in browser storage under
schemaflow-session. - Import validation expects
modelsandrelationshipsarrays. - Share links are URL-compressed and may exceed practical browser limits for large schemas.