# How the Graph Export Feature in code-graph-rag Works for External Analysis Tools

> Learn how code-graph-rag's graph export feature converts your code graph to portable JSON for external analysis. Seamlessly integrate with your tools using Cypher queries and Memgraph.

- Repository: [Vitali Avagyan/code-graph-rag](https://github.com/vitali87/code-graph-rag)
- Tags: how-to-guide
- Published: 2026-08-16

---

**The graph export feature in code-graph-rag converts an in-memory code graph into a portable JSON file that any external tool can ingest, using Cypher queries to extract nodes and relationships from Memgraph and serializing them through `GraphService.export_graph_to_dict()` and `export_graph_to_file()`.**

The **code-graph-rag** repository by vitali87 builds rich knowledge graphs from codebases and stores them in Memgraph. A critical capability is exporting these graphs for external analysis—whether for visualization, static analysis benchmarks, or feeding structured code relationships into LLM pipelines. This article explains the complete export architecture, from Cypher query execution to JSON file generation.

---

## Core Export Architecture

The export pipeline consists of five coordinated components that transform database state into a self-contained JSON artifact.

### 1. MemgraphIngestor Triggers Export

The `MemgraphIngestor` class manages the lifecycle of graph data in Memgraph. When export is requested, it delegates to `GraphService.export_graph_to_dict()` to materialize the entire graph into Python data structures.

In [`codebase_rag/graph_updater.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/graph_updater.py), the ingestor maintains the database connection handle that `GraphService` uses for query execution.

### 2. GraphService Executes Cypher Queries

The `export_graph_to_dict()` method in [`codebase_rag/services/graph_service.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/services/graph_service.py) (lines 60–78) performs the heavy lifting:

```python

# From codebase_rag/services/graph_service.py

def export_graph_to_dict(self) -> Dict[str, Any]:
    """Export the entire graph as a dictionary for external use."""
    nodes = self._fetch_nodes()
    relationships = self._fetch_relationships()
    metadata = self._build_metadata(len(nodes), len(relationships))
    
    return {
        "nodes": nodes,
        "relationships": relationships,
        "metadata": metadata
    }

```

Two pre-defined Cypher queries retrieve the raw graph data:

- **`CYPHER_EXPORT_NODES`** – Returns every node with its ID, labels, and property map
- **`CYPHER_EXPORT_RELATIONSHIPS`** – Returns every relationship with source ID, target ID, type, and properties

These queries use Memgraph's `map` projection to ensure JSON-serializable output formats directly from the database.

### 3. Metadata and Timestamp Generation

The `_build_metadata()` helper constructs a `GraphMetadata` dataclass containing:

| Field | Description |
|-------|-------------|
| `total_nodes` | Count of nodes exported |
| `total_relationships` | Count of relationships exported |
| `exported_at` | ISO 8601 timestamp from `_get_current_timestamp()` (UTC) |

The timestamp is generated once per export, ensuring consistency across all serialised components.

### 4. export_graph_to_file Serializes to Disk

The `export_graph_to_file()` function in [`codebase_rag/main.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/main.py) (lines 1400–1410) wraps the dictionary export with file I/O:

```python

# From codebase_rag/main.py

def export_graph_to_file(ingestor, output_path: str) -> bool:
    """Export graph to a JSON file for external analysis tools."""
    try:
        graph_data = ingestor.export_graph_to_dict()
        with open(output_path, 'w', encoding='utf-8') as f:
            json.dump(graph_data, f, indent=2, ensure_ascii=False)
        logger.info(f"Graph exported successfully to {output_path}")
        return True
    except Exception as e:
        logger.error(f"Failed to export graph: {e}")
        return False

```

Error handling is explicit: exceptions are logged and converted to boolean status, allowing CLI and programmatic callers to implement retry logic or fallback behavior.

### 5. CLI Entry Point

The `--export` flag in [`codebase_rag/cli.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/cli.py) (lines 320–340) exposes this functionality to shell users:

```python

# From codebase_rag/cli.py

if args.export:
    success = export_graph_to_file(ingestor, args.export)
    sys.exit(0 if success else 1)

```

---

## JSON Output Schema

Exported files follow a predictable structure that external tools can rely on:

```json
{
  "nodes": [
    {
      "id": "func://src/utils.py::calculate_hash",
      "labels": ["Function", "Callable"],
      "properties": {
        "name": "calculate_hash",
        "file_path": "src/utils.py",
        "line_number": 45,
        "docstring": "Compute BLAKE2b hash of input data."
      }
    }
  ],
  "relationships": [
    {
      "source_id": "func://src/utils.py::calculate_hash",
      "target_id": "func://src/crypto.py::blake2b",
      "type": "CALLS",
      "properties": {
        "confidence": 1.0,
        "static": true
      }
    }
  ],
  "metadata": {
    "total_nodes": 1247,
    "total_relationships": 3892,
    "exported_at": "2024-01-15T09:23:17.342Z"
  }
}

```

This schema is **self-describing**: no external schema registry or Memgraph connection is required to interpret the file.

---

## Practical Usage Examples

### Export via Command Line

```bash

# Basic export to current directory

python -m codebase_rag.main --export ./my_project_graph.json

# Verify export succeeded

jq '.metadata' my_project_graph.json

```

### Programmatic Export in Python

```python
from codebase_rag.graph_updater import MemgraphIngestor
from codebase_rag.main import export_graph_to_file

# Initialize with existing Memgraph connection

ingestor = MemgraphIngestor(uri="bolt://localhost:7687")

# Export to file

success = export_graph_to_file(ingestor, "code_graph.json")
assert success, "Export failed"

# Or work with the dictionary directly

graph_dict = ingestor.export_graph_to_dict()
print(f"Exported {graph_dict['metadata']['total_nodes']} nodes")

```

### Loading into NetworkX for Analysis

```python
import json
import networkx as nx

# Load exported graph

with open("code_graph.json") as f:
    data = json.load(f)

# Build NetworkX directed graph

G = nx.DiGraph()

# Add nodes with all properties

for node in data["nodes"]:
    G.add_node(node["id"], labels=node["labels"], **node["properties"])

# Add edges with relationship type and properties

for rel in data["relationships"]:
    G.add_edge(
        rel["source_id"],
        rel["target_id"],
        relation_type=rel["type"],
        **rel["properties"]
    )

# Run external analysis: find most called functions

in_degree = dict(G.in_degree())
most_called = max(in_degree, key=in_degree.get)
print(f"Most called function: {most_called} ({in_degree[most_called]} callers)")

```

### Importing into Neo4j for Visualization

```python
from py2neo import Graph, Node, Relationship
import json

g = Graph("bolt://localhost:7687", auth=("neo4j", "password"))

with open("code_graph.json") as f:
    data = json.load(f)

# Create nodes

node_map = {}
for n in data["nodes"]:
    node = Node(*n["labels"], **n["properties"])
    g.create(node)
    node_map[n["id"]] = node

# Create relationships

for r in data["relationships"]:
    src = node_map[r["source_id"]]
    tgt = node_map[r["target_id"]]
    g.create(Relationship(src, r["type"], tgt, **r.get("properties", {})))

```

---

## Reliability and Performance Considerations

### Memory Limits for Large Graphs

`GraphService` applies `settings.QUERY_MEMORY_LIMIT_MB` before executing Cypher queries, preventing unbounded memory consumption when exporting massive codebases.

### Consistent Error Handling

Export failures are logged with structured messages (`ls.MG_EXPORTING`, `ls.MG_EXPORTED` from localization strings) and propagated as boolean return values. This supports:

- Retry logic with exponential backoff
- Fallback to partial exports
- Pipeline failure notifications in CI/CD systems

### Timestamp Consistency

The UTC timestamp is captured once at export start, not per-query. This ensures that `metadata.exported_at` accurately represents a single logical snapshot, even if node and relationship queries execute sequentially.

---

## Key Source Files

| File | Purpose | Direct Link |
|------|---------|-------------|
| [`codebase_rag/services/graph_service.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/services/graph_service.py) | Core export logic: `export_graph_to_dict()`, Cypher query execution, metadata construction | [View source](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/services/graph_service.py) |
| [`codebase_rag/main.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/main.py) | File serialization wrapper: `export_graph_to_file()` with error handling | [View source](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/main.py) |
| [`codebase_rag/cli.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/cli.py) | Command-line interface exposing `--export` flag | [View source](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/cli.py) |
| [`codebase_rag/graph_updater.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/graph_updater.py) | `MemgraphIngestor` class that orchestrates database operations | [View source](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/graph_updater.py) |
| [`evals/cgr_graph.py`](https://github.com/vitali87/code-graph-rag/blob/main/evals/cgr_graph.py) | Evaluation harness consuming exported graphs for RAG benchmarks | [View source](https://github.com/vitali87/code-graph-rag/blob/main/evals/cgr_graph.py) |
| [`codebase_rag/tests/test_main_helpers_unit.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/tests/test_main_helpers_unit.py) | Unit tests verifying export functionality and edge cases | [View source](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/tests/test_main_helpers_unit.py) |

---

## Summary

- **`export_graph_to_dict()`** in [`graph_service.py`](https://github.com/vitali87/code-graph-rag/blob/main/graph_service.py) queries Memgraph using `CYPHER_EXPORT_NODES` and `CYPHER_EXPORT_RELATIONSHIPS`, returning a structured dictionary with nodes, relationships, and metadata
- **`export_graph_to_file()`** in [`main.py`](https://github.com/vitali87/code-graph-rag/blob/main/main.py) handles JSON serialization and file I/O with explicit error handling
- **CLI integration** via `--export` flag makes the feature accessible without writing code
- **Self-describing JSON output** requires no live database connection for external tools to consume
- **Memory limits and consistent timestamps** ensure reliable operation on large codebases

---

## Frequently Asked Questions

### What format does the graph export produce?

The export produces **JSON** with a stable three-part structure: `nodes` (array of node objects), `relationships` (array of relationship objects), and `metadata` (export statistics and timestamp). This format is natively supported by Python's `json` module, JavaScript, and most graph analysis libraries.

### Can I export without running the full ingestion pipeline?

**No**—the export requires an active `MemgraphIngestor` instance with a populated database. The export queries read from Memgraph, not from cached intermediate files. To export a codebase, you must first complete ingestion, then call the export function.

### How large can exported graphs be?

The system handles large graphs through two mechanisms: **query-level memory limits** (`settings.QUERY_MEMORY_LIMIT_MB`) prevent database-side overload, and **streaming JSON serialization** avoids loading the entire export into RAM before writing. For multi-million node graphs, consider exporting subgraphs using filtered Cypher queries directly against Memgraph.

### Which external tools are compatible with the export format?

Any tool that ingests JSON works directly. Verified use cases include **NetworkX** (Python graph algorithms), **Neo4j** (import via [`apoc.import.json`](https://github.com/vitali87/code-graph-rag/blob/main/apoc.import.json) or py2neo), **Graphistry** (visualization), **Gephi** (via JSON → GEXF conversion), and custom LLM pipelines that parse the structure for retrieval-augmented generation.