# How to Implement SQLite Knowledge Graphs with Typed Edge Traversal

> Implement typed edge traversal in SQLite with a clear, step-by-step guide. Learn to model relationships and query your knowledge graph efficiently using recursive CTEs.

- Repository: [Anthropic/claude-plugins-community](https://github.com/anthropics/claude-plugins-community)
- Tags: how-to-guide
- Published: 2026-09-12

---

**Implement typed edge traversal in SQLite by modeling each relationship type as distinct tables with foreign key constraints to a central nodes table, then query using recursive CTEs that target specific edge tables.**

Implementing **SQLite knowledge graphs with typed edge traversal** requires careful schema design to support efficient queries across different relationship types. The Claude Plugins Community repository demonstrates the power of typed, declarative specifications in its plugin manifests, and you can apply these same architectural principles to create a lightweight graph database that enforces referential integrity while enabling complex multi-hop queries.

## Designing the Typed Edge Schema

The foundation of a SQLite knowledge graph rests on separating node entities from typed relationships. This approach mirrors the structured JSON manifests found in the Claude Plugins Community repository, where each plugin capability is explicitly typed and declared.

### The Nodes Table

Create a central table to store all graph entities:

```sql
CREATE TABLE nodes (
    id   INTEGER PRIMARY KEY,
    label TEXT NOT NULL,
    type  TEXT
);

```

### Separate Tables per Edge Type

For optimal performance and clarity, create distinct tables for each relationship type (e.g., `edges_parent_child`, `edges_related`, `edges_authored_by`). This design allows SQLite to maintain separate indexes per edge type and makes queries self-documenting.

```sql
CREATE TABLE edges_parent_child (
    src_id INTEGER NOT NULL,
    dst_id INTEGER NOT NULL,
    FOREIGN KEY (src_id) REFERENCES nodes(id),
    FOREIGN KEY (dst_id) REFERENCES nodes(id),
    PRIMARY KEY (src_id, dst_id)
);

```

**Performance:** SQLite can use separate indexes per edge type, reducing scan cost. **Clarity:** Queries clearly express relationship semantics through table names.

### Alternative: Single Table with Type Discriminator

If your edge types evolve dynamically, use a unified table with a discriminator column:

```sql
CREATE TABLE edges (
    src_id INTEGER NOT NULL,
    dst_id INTEGER NOT NULL,
    type   TEXT NOT NULL,
    FOREIGN KEY (src_id) REFERENCES nodes(id),
    FOREIGN KEY (dst_id) REFERENCES nodes(id),
    PRIMARY KEY (src_id, dst_id, type)
);

```

## Populating the Knowledge Graph

Insert nodes and establish relationships using standard SQL:

```sql
INSERT INTO nodes (id, label, type) VALUES
    (1, 'Apple', 'Fruit'),
    (2, 'Fruit', 'Category'),
    (3, 'Food', 'Category');

INSERT INTO edges_parent_child (src_id, dst_id) VALUES
    (1, 2),
    (2, 3);

```

## Implementing Typed Edge Traversal

Traversal leverages recursive Common Table Expressions (CTEs). The structure depends on whether you traverse a single relationship type or multiple types.

### Single-Type Traversal

Query hierarchical relationships using a recursive CTE targeting a specific edge table:

```sql
WITH RECURSIVE hierarchy(id, depth) AS (
    SELECT dst_id, 1 FROM edges_parent_child WHERE src_id = ?
    UNION ALL
    SELECT e.dst_id, h.depth + 1
    FROM edges_parent_child e
    JOIN hierarchy h ON e.src_id = h.id
)
SELECT n.* FROM hierarchy h
JOIN nodes n ON n.id = h.id;

```

### Multi-Type Traversal

Cross multiple relationship types by combining edge tables with `UNION ALL`:

```sql
WITH RECURSIVE graph(id, depth) AS (
    SELECT dst_id, 1 FROM edges_parent_child WHERE src_id = ?
    UNION ALL
    SELECT dst_id, 1 FROM edges_related WHERE src_id = ?
    UNION ALL
    SELECT e.dst_id, g.depth + 1
    FROM (
        SELECT * FROM edges_parent_child
        UNION ALL
        SELECT * FROM edges_related
    ) e
    JOIN graph g ON e.src_id = g.id
)
SELECT n.* FROM graph g
JOIN nodes n ON n.id = g.id;

```

### Filtering by Type in Unified Schemas

When using a single `edges` table, constrain the recursive CTE to a specific relationship type:

```sql
WITH RECURSIVE walk(id, depth) AS (
    SELECT dst_id, 1 FROM edges WHERE src_id = ? AND type = 'parent_child'
    UNION ALL
    SELECT e.dst_id, w.depth + 1
    FROM edges e
    JOIN walk w ON e.src_id = w.id
    WHERE e.type = 'parent_child'
)
SELECT n.* FROM walk w
JOIN nodes n ON n.id = w.id;

```

## Optimizing Traversal Performance

Efficient graph walks depend on proper indexing strategy, schema versioning, and memory configuration.

### Indexing Edge Sources

Create indexes on the source columns of each edge table to accelerate outgoing edge lookups:

```sql
CREATE INDEX idx_parent_child_src ON edges_parent_child(src_id);
CREATE INDEX idx_related_src    ON edges_related(src_id);

```

### Schema Versioning

Maintain a `schema_version` table to track migrations as your graph structure evolves:

```sql
CREATE TABLE schema_version (
    version INTEGER PRIMARY KEY,
    applied_at TEXT DEFAULT CURRENT_TIMESTAMP
);

```

### Memory and Bulk Loading

For large graphs, optimize insertion performance using `INSERT … VALUES (…), (…)` syntax or the SQLite `.import` command. Configure the cache size to hold frequently accessed edges in memory:

```sql
PRAGMA cache_size = 10000;

```

## Repository Context: Typed Specifications in Practice

The Claude Plugins Community repository emphasizes typed, declarative specifications in its plugin architecture, providing an analogous mental model for SQLite knowledge graph design. While the repository (`anthropics/claude-plugins-community`) primarily hosts plugin definitions in [`README.md`](https://github.com/anthropics/claude-plugins-community/blob/main/README.md) and the [`.claude-plugin/marketplace.json`](https://github.com/anthropics/claude-plugins-community/blob/main/.claude-plugin/marketplace.json) manifest, the structured approach to capability definitions in files like `tres-finance-plugin/skills/*/SKILL.md` demonstrates how explicit typing creates maintainable systems. By modeling each edge type as a distinct table—similar to how distinct plugin skills are declared in separate documentation files—you create a self-documenting schema that scales cleanly.

## Summary

- **Separate edge tables** for each relationship type optimize query performance and schema clarity in SQLite knowledge graphs.
- **Foreign key constraints** from edge tables to the central `nodes` table enforce referential integrity across the graph.
- **Recursive CTEs** enable efficient multi-hop traversal when targeting specific typed edge tables or combining multiple types with `UNION ALL`.
- **Source column indexes** are essential for fast traversal operations, as they minimize scan costs when locating outgoing edges.
- **Typed specifications** in the Claude Plugins Community repository demonstrate architectural patterns applicable to database schema design.

## Frequently Asked Questions

### Should I use separate tables or a single table with a type column for edges?

Use **separate tables** when relationship types are stable and query patterns typically access one type at a time, as this enables optimal indexing and clear semantics. Use a **single table with a type discriminator** when edge types are dynamic or frequently queried together, though this requires filtering on the type column during traversal.

### How do I handle dynamic edge types that change at runtime?

Implement a single `edges` table with a `type` column and include `type` in your recursive CTE's `WHERE` clause to filter traversals. Maintain a registry table tracking available edge types if your application dynamically discovers relationships.

### What indexing strategy is essential for fast graph traversal?

Create indexes on the **source node columns** (e.g., `src_id`) of every edge table. Traversal operations primarily locate outgoing edges from a given node, and indexing `src_id` transforms these lookups from full table scans to index seeks.

### How does the Claude Plugins Community repository relate to SQLite knowledge graphs?

The repository's structure—particularly the typed JSON manifests in [`.claude-plugin/marketplace.json`](https://github.com/anthropics/claude-plugins-community/blob/main/.claude-plugin/marketplace.json) and structured skill definitions in paths like `tres-finance-plugin/skills/*/SKILL.md`—exemplifies the same declarative typing principles. Just as the repository separates concerns into explicitly typed files, your SQLite schema should separate edge types into distinct tables for maintainability and performance.