vdb

An interactive database ERD visualiser for Rails apps. Reads your db/schema.rb, infers foreign-key relationships, and renders a D3 force-directed diagram at any path you choose.

Development use only. Do not mount this in production without authentication.

ERD overview

→ See the example app — a blog schema (users / posts / reviewers / reviews / comments / tags) that shows a real-world vdb integration.


Features

  • Auto-parses db/schema.rb (and optional extra schema files)
  • Infers FK relationships from _id column naming and add_foreign_key statements
  • Crow's-foot / single-tick cardinality notation
  • Table search, zoom in/out, fit-to-screen, drag-to-rearrange
  • Positions persisted to localStorage per database
  • Click a table to highlight its relationships
  • Optional HTTP Basic Auth
  • Zero asset-pipeline dependencies — D3 and Stimulus loaded from CDN

Installation

Add to your Gemfile, inside the development group:

group :development do
  gem 'vdb', path: 'vendor/gems/vdb'  # local path
  # or once published:
  # gem 'vdb'
end

Run:

bundle install

Setup

1. Mount the engine

In config/routes.rb:

Rails.application.routes.draw do
  # Recommended: scope under a path and/or constrain to development
  if Rails.env.development?
    mount Vdb::Engine, at: '/dev/erd'
  end

  # rest of your routes…
end

2. Optional initializer

Create config/initializers/vdb.rb only if you need to change defaults:

# config/initializers/vdb.rb

# Only required in development — skip the initializer entirely in other envs.
return unless Rails.env.development?

Vdb.configure do |c|
  # HTTP Basic Auth. Leave username nil to disable.
  c.username = ENV.fetch('VDB_USER', nil)
  c.password = ENV.fetch('VDB_PASS', nil)

  # Additional schema files to expose as tabs (label => path).
  # 'primary' auto-resolves to db/schema.rb if path is nil.
  c.databases = {
    'primary' => nil,                                   # db/schema.rb (default)
    'audit'   => Rails.root.join('db', 'audit_schema.rb')
  }

  # Page title shown in the header.
  c.title = 'Database ERD'
end

3. Visit the ERD

http://localhost:3000/dev/erd

Configuration reference

Option Type Default Description
username `String \ nil` nil
password `String \ nil` nil
databases `Hash<String, Pathname\ String\ nil>`
title String 'Database ERD' Title shown in the browser and page header.

Routing helpers

Inside the engine:

Helper Path
vdb.root_path /dev/erd
vdb.root_path(database: 'audit') /dev/erd?database=audit
vdb.parse_path /dev/erd/parse

How it works

  1. Schema parsingVdb::SchemaToGraph reads the schema file line-by-line. It extracts create_table blocks (columns + types), add_foreign_key declarations, and add_index … unique: true constraints.
  2. FK inference — any *_id column whose referenced plural table exists in the schema becomes an implicit link (unless an explicit add_foreign_key already covers it).
  3. Cardinalitymany → 1 by default. If the FK column has a single-column unique index, it becomes 1 → 1.
  4. Rendering — a Stimulus controller drives a D3 force-directed simulation. Node positions are saved to localStorage keyed by database name.

Screenshots

All screenshots below are taken from the bundled example app — a Rails 8 blog with users, posts, reviewers, reviews, comments, and tags.

Full ERD — force-directed graph

ERD wide view

All seven tables laid out automatically by D3's force simulation. Foreign-key lines carry crow's-foot (N) and single-tick (1) cardinality markers. FK columns are highlighted in cyan.

Search filtered to "post"

Typing in the search box dims non-matching tables so you can quickly locate what you need in a large schema.

Relationship highlighting

posts table highlighted

Click any table header to highlight its direct neighbours and relationships; everything else fades back.

Example app pages

Posts list Post detail
Posts Post detail

Excluded tables

The following Rails-internal and Solid* tables are hidden automatically:

  • active_storage_*, action_text_rich_texts
  • ar_internal_metadata, schema_migrations
  • solid_cache_*, solid_queue_*, solid_cable_*

Security note

This gem renders your database schema — never mount it in production without authentication. The recommended pattern is to wrap the mount in if Rails.env.development? and optionally add Basic Auth via the config.