# How DBX's ER Diagram Visualizes Table Relationships: Chen-Style SVG Generation

> DBX visualizes table relationships with Chen-style SVG connectors. Learn how DBX scans foreign keys to render ER diagrams, showing referential integrity and cardinality between tables.

- Repository: [skyler/dbx](https://github.com/t8y2/dbx)
- Tags: deep-dive
- Published: 2026-07-09

---

**DBX renders table relationships by scanning foreign key definitions to create `DiagramRelationship` records, positioning entities as SVG rectangles, and drawing Chen-style diamond connectors with cardinality markers (1/N) to represent referential integrity between tables.**

The open-source DBX database tool (t8y2/dbx) automatically generates Entity-Relationship diagrams using Chen notation conventions. This article examines the TypeScript implementation that transforms database schema metadata into interactive SVG visualizations, detailing the three-stage pipeline that converts foreign keys into visual relationships.

## Extracting Foreign Key Relationships

The visualization process begins in [`src/apps/desktop/src/lib/diagram/erDiagram.ts`](https://github.com/t8y2/dbx/blob/main/src/apps/desktop/src/lib/diagram/erDiagram.ts) with the `buildDiagramRelationships` function. This utility scans the table list, filters for foreign keys referencing visible tables, and constructs standardized relationship objects.

The function uses `flatMap` to iterate through tables and their foreign key arrays, creating a `DiagramRelationship` for each valid reference:

```typescript
// src/apps/desktop/src/lib/diagram/erDiagram.ts
const foreignKeyRelationships = tables.flatMap(t =>
  t.foreignKeys
    .filter(fk => visibleTableNames.has(fk.ref_table))
    .map(fk => ({
      id: relationshipId(t.name, fk),
      name: fk.name,
      kind: "foreign-key",
      sourceTable: t.name,
      sourceColumn: fk.column,
      targetTable: fk.ref_table,
      targetColumn: fk.ref_column,
      sourceCardinality: "N",
      targetCardinality: "1",
    }))
);

```

This array combines with user-defined custom relationships before proceeding to the layout stage. Each relationship explicitly defines **source cardinality** as "N" and **target cardinality** as "1", following standard one-to-many relationship semantics.

## Constructing Chen-Style Diagram Nodes

The `buildEngineeringDiagram` function in [`src/apps/desktop/src/lib/diagram/engineeringDiagram.ts`](https://github.com/t8y2/dbx/blob/main/src/apps/desktop/src/lib/diagram/engineeringDiagram.ts) transforms abstract relationships into concrete SVG node objects. The implementation creates three distinct node types to represent the Chen notation:

- **EngineeringEntityNode**: Centered rectangles (`<rect>`) representing database tables
- **EngineeringAttributeNode**: Small rectangles positioned around entities for columns (primary keys, foreign keys, etc.)
- **EngineeringRelationshipNode**: Diamond shapes (`<polygon>`) placed midway between connected entities

The relationship node calculation positions the diamond at the midpoint between source and target entities:

```typescript
// src/apps/desktop/src/lib/diagram/engineeringDiagram.ts
const relationshipNodes = relationships.map(rel => ({
  id: rel.id,
  label: relationshipLabel(rel),
  sourceTable: rel.sourceTable,
  targetTable: rel.targetTable,
  sourceCardinality: rel.sourceCardinality,
  targetCardinality: rel.targetCardinality,
  x: (sourceCenter.x + targetCenter.x) / 2 - ENGINEERING_RELATIONSHIP_WIDTH / 2,
  y: (sourceCenter.y + targetCenter.y) / 2 - ENGINEERING_RELATIONSHIP_HEIGHT / 2,
  width: ENGINEERING_RELATIONSHIP_WIDTH,
  height: ENGINEERING_RELATIONSHIP_HEIGHT,
}));

```

The diamond configuration includes the relationship name (or source column label) and maintains references to both cardinalities for rendering the "1" and "N" markers.

## Rendering SVG Output

The final stage occurs in [`apps/desktop/src/lib/export/diagramSvgExport.ts`](https://github.com/t8y2/dbx/blob/main/apps/desktop/src/lib/export/diagramSvgExport.ts) where `buildEngineeringDiagramSvg` converts the node objects into standard SVG markup. The generated diagram contains specific geometric primitives:

- `<rect>` elements for table entities
- `<ellipse>` elements for "1" cardinality markers
- `<polygon>` elements for "N" cardinality markers
- `<text>` elements labeling tables, columns, and cardinalities

The unit test suite in [`packages/app-tests/diagramSvgExport.test.ts`](https://github.com/t8y2/dbx/blob/main/packages/app-tests/diagramSvgExport.test.ts) validates the presence of these Chen notation markers:

```typescript
test("exports the engineering ER diagram with Chen-style shapes and cardinalities", () => {
  const diagram = buildEngineeringDiagram(tables, relationships, positions);
  const svg = buildEngineeringDiagramSvg(diagram);
  assert.match(svg, /<ellipse /);   // "1" marker
  assert.match(svg, /<polygon /);   // "N" marker
  assert.match(svg, /<rect /);      // table entity
});

```

## Complete Implementation Workflow

The following workflow demonstrates the end-to-end process from schema extraction to SVG download:

```typescript
import {
  buildDiagramRelationships,
  layoutDiagramTables,
} from "@/lib/diagram/erDiagram";
import { buildEngineeringDiagram, ENGINEERING_ENTITY_WIDTH } from "@/lib/diagram/engineeringDiagram";
import { buildEngineeringDiagramSvg } from "@/lib/export/diagramSvgExport";

// 1️⃣ Gather tables (fetched from DBX schema API)
const tables = await getTablesFromBackend(); // DiagramTable[]

// 2️⃣ Build relationships (FK + custom)
const relationships = buildDiagramRelationships(tables);

// 3️⃣ Compute a simple layout (optional, DBX remembers user‑dragged positions)
const positions = layoutDiagramTables(tables.map(t => ({ name: t.name, columns: t.columns })));

// 4️⃣ Build the full diagram model
const diagram = buildEngineeringDiagram(tables, relationships, positions);

// 5️⃣ Export SVG – ready for download, copy‑paste or embedding in docs
const svg = buildEngineeringDiagramSvg(diagram);
downloadFile(svg, "my‑er‑diagram.svg");

```

## Key Source Files

| File | Purpose |
|------|---------|
| [`apps/desktop/src/lib/diagram/erDiagram.ts`](https://github.com/t8y2/dbx/blob/main/apps/desktop/src/lib/diagram/erDiagram.ts) | Detects foreign‑key and custom links; creates `DiagramRelationship` objects. |
| [`apps/desktop/src/lib/diagram/engineeringDiagram.ts`](https://github.com/t8y2/dbx/blob/main/apps/desktop/src/lib/diagram/engineeringDiagram.ts) | Converts tables & relationships into Chen‑style SVG node objects (entities, attributes, diamonds). |
| [`apps/desktop/src/lib/export/diagramSvgExport.ts`](https://github.com/t8y2/dbx/blob/main/apps/desktop/src/lib/export/diagramSvgExport.ts) | Renders the diagram model as an SVG string and handles safe file naming. |
| [`packages/app-tests/diagramSvgExport.test.ts`](https://github.com/t8y2/dbx/blob/main/packages/app-tests/diagramSvgExport.test.ts) | Test suite confirming the presence of `<ellipse>`, `<polygon>`, and `<rect>` in the exported SVG. |

## Summary

- **Foreign key detection** occurs in `buildDiagramRelationships`, which filters visible tables and creates standardized relationship records with N:1 cardinality.
- **Chen notation rendering** uses diamond-shaped `EngineeringRelationshipNode` objects positioned between entity rectangles, with ellipse and polygon markers indicating cardinality.
- **SVG generation** produces standards-compliant markup using `<rect>`, `<ellipse>`, and `<polygon>` elements, validated by the test suite to ensure correct shape representation.
- **Implementation** requires three main phases: relationship extraction, node construction with midpoint calculations, and SVG string conversion.

## Frequently Asked Questions

### What notation style does DBX use for ER diagrams?

DBX implements **Chen notation** for its engineering diagrams. This style represents entities as rectangles, relationships as diamonds, and uses explicit cardinality markers (1 and N) to indicate relationship multiplicity, as opposed to Crow's Foot or UML notation styles.

### How does DBX determine cardinality between tables?

The system automatically assigns **"N" cardinality to the source table** and **"1" cardinality to the target table** (the referenced table) for all foreign key relationships. This convention follows standard relational database design where foreign keys create many-to-one relationships between tables.

### Can DBX visualize relationships beyond foreign keys?

Yes. The `buildDiagramRelationships` function combines foreign key relationships with **user-defined custom relationships**. These custom links allow visualization of logical connections that may not exist as formal foreign key constraints in the database schema.

### What SVG elements represent relationships in the exported diagram?

According to the source code in [`diagramSvgExport.ts`](https://github.com/t8y2/dbx/blob/main/diagramSvgExport.ts), relationships use **`<polygon>` elements for the diamond shape** and **`<ellipse>` elements for the "1" cardinality markers**. The connecting entities use `<rect>` elements, while all labels use `<text>` elements.