Developer Guide

The complete technical reference for developing applications with Geode graph database. This guide covers API documentation, query syntax, client libraries, architectural patterns, and production best practices for building scalable graph applications.

Architecture Overview

Geode is built on modern, production-ready foundations:

Core Technology:

  • Written in Zig for performance and safety
  • QUIC transport with TLS 1.3 encryption
  • Protobuf wire protocol for client-server communication
  • ACID-compliant transaction engine
  • ISO/IEC 39075:2024 GQL standard compliance

Key Components:

  • Query Parser & Optimizer
  • Storage Engine with MVCC
  • Transaction Manager
  • Security & Authorization Layer
  • Client Protocol Handler

Architecture Characteristics:

  • Memory-mapped I/O for efficient storage access
  • SIMD-accelerated vector operations
  • Six specialized index types for different workloads

Query Language Reference

GQL Syntax Overview

Graph Query Language (GQL) is a declarative language for querying property graphs. Unlike SQL’s table-oriented model, GQL thinks in terms of graph patterns.

MATCH Clause

Find patterns in the graph:

-- Basic node match
MATCH (p:Person)
RETURN p

-- Match with property filter
MATCH (p:Person {name: 'Alice'})
RETURN p

-- Match relationship
MATCH (a:Person)-[:KNOWS]->(b:Person)
RETURN a.name, b.name

-- Multi-hop traversal
MATCH (a:Person)-[:KNOWS*2]->(b:Person)
RETURN a.name, b.name

-- Variable-length with bounds
MATCH (a)-[:KNOWS*1..3]->(b)
RETURN a, b

-- Undirected matching
MATCH (a)-[:KNOWS]-(b)  -- Either direction
RETURN a, b

CREATE Clause

Insert nodes and relationships:

-- Create single node
CREATE (p:Person {name: 'Alice', age: 30})

-- Create multiple nodes
CREATE (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'})

-- Create with relationship
CREATE (a:Person {name: 'Alice'})-[:KNOWS]->(b:Person {name: 'Bob'})

-- Create relationship between existing nodes
MATCH (a:Person {name: 'Alice'})
MATCH (b:Person {name: 'Bob'})
CREATE (a)-[:KNOWS {since: 2020}]->(b)

SET Clause

Update properties:

-- Set single property
MATCH (p:Person {name: 'Alice'})
SET p.age = 31

-- Set multiple properties
MATCH (p:Person {name: 'Alice'})
SET p.age = 31, p.city = 'NYC', p.updated_at = timestamp()

-- Add new properties
MATCH (p:Person {name: 'Alice'})
SET p += {email: 'alice@example.com', verified: true}

DELETE Clause

Remove nodes and relationships:

-- Delete relationship
MATCH (a:Person)-[r:KNOWS]->(b:Person)
WHERE a.name = 'Alice' AND b.name = 'Bob'
DELETE r

-- Delete node (must have no relationships)
MATCH (p:Person {name: 'Alice'})
DELETE p

-- Delete node and all relationships
MATCH (p:Person {name: 'Alice'})
DETACH DELETE p

WHERE Clause

Filter results:

-- Property comparison
MATCH (p:Person)
WHERE p.age > 25
RETURN p

-- Multiple conditions
MATCH (p:Person)
WHERE p.age > 25 AND p.city = 'NYC'
RETURN p

-- IN operator
MATCH (p:Person)
WHERE p.name IN ['Alice', 'Bob', 'Carol']
RETURN p

-- Pattern predicate
MATCH (p:Person)
WHERE EXISTS { MATCH (p)-[:KNOWS]->(:Person {city: 'NYC'}) }
RETURN p

-- Null checks
MATCH (p:Person)
WHERE p.email IS NOT NULL
RETURN p

Aggregation

Compute aggregate values:

-- Count
MATCH (p:Person)
RETURN COUNT(p)

-- Count distinct
MATCH (p:Person)
RETURN COUNT(DISTINCT p.city)

-- Sum, Average
MATCH (o:Order)
RETURN SUM(o.total), AVG(o.total)

-- Min, Max
MATCH (p:Product)
RETURN MIN(p.price), MAX(p.price)

-- Group by
MATCH (p:Person)
RETURN p.city, COUNT(p) as residents
ORDER BY residents DESC

-- Collect into list
MATCH (p:Person)-[:KNOWS]->(friend)
RETURN p.name, COLLECT(friend.name) as friends

Subqueries

Compose complex queries:

-- Existential subquery
MATCH (p:Person)
WHERE EXISTS {
    MATCH (p)-[:KNOWS]->(:Person {city: 'NYC'})
}
RETURN p

-- Scalar subquery
MATCH (p:Person)
RETURN p.name, (
    MATCH (p)-[:KNOWS]->(friend)
    RETURN COUNT(friend)
) as friend_count

-- WITH clause for chaining
MATCH (p:Person)
WITH p, SIZE((p)-[:KNOWS]->()) as friend_count
WHERE friend_count > 5
RETURN p.name, friend_count

OPTIONAL MATCH

Handle nullable patterns:

-- Optional relationship
MATCH (u:User)
OPTIONAL MATCH (u)-[:HAS_AVATAR]->(img:Image)
RETURN u.name, img.url

-- Multiple optional patterns
MATCH (u:User)
OPTIONAL MATCH (u)-[:HAS_AVATAR]->(img:Image)
OPTIONAL MATCH (u)-[:HAS_PROFILE]->(prof:Profile)
RETURN u.name, img.url, prof.bio

Client Libraries

Python Client

Installation:

pip install geode-client

Basic Usage:

import asyncio
from geode_client import Client

async def main():
    client = Client(host="localhost", port=3141)
    async with client.connection() as conn:
        # Execute query
        result, _ = await conn.query(
            "MATCH (p:Person {name: $name}) RETURN p",
            name="Alice"
        )

        # Iterate results
        for row in result.rows:
            print(row['p'])

        # Transactions
        async with client.connection() as tx:
            await tx.begin()
            await tx.execute("CREATE (p:Person {name: $name})", name="Bob")
            await tx.execute("CREATE (p:Person {name: $name})", name="Carol")

asyncio.run(main())

Connection Pooling:

client = Client("localhost:3141", pool_size=100, pool_timeout=30)

Error Handling:

from geode_client import QueryError, TransactionConflictError

try:
    await client.execute(query)
except TransactionConflictError:
    # Retry logic
    pass
except QueryError as e:
    # Handle other errors
    if e.is_syntax_error:
        logger.error(f"Syntax error: {e}")

Go Client

Installation:

go get geodedb.com/geode

Basic Usage:

import (
    "database/sql"
    _ "geodedb.com/geode"
)

func main() {
    db, err := sql.Open("geode", "quic://localhost:3141")
    if err != nil {
        panic(err)
    }
    defer db.Close()

    // Query
    rows, err := db.Query("MATCH (p:Person {name: $name}) RETURN p",
        sql.Named("name", "Alice"))
    defer rows.Close()

    // Transaction
    tx, _ := db.Begin()
    tx.Exec("CREATE (p:Person {name: $name})", sql.Named("name", "Bob"))
    tx.Commit()
}

Connection Pool Configuration:

db.SetMaxOpenConns(100)
db.SetMaxIdleConns(10)
db.SetConnMaxLifetime(time.Hour)

Rust Client

Installation:

[dependencies]
geode-client = "0.18"
tokio = { version = "1", features = ["full"] }

Basic Usage:

use geode_client::{Client, params};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = Client::connect("localhost:3141").await?;

    // Query with parameters
    let mut results = client.execute(
        "MATCH (p:Person {name: $name}) RETURN p",
        params! { "name" => "Alice" }
    ).stream().await?;

    while let Some(row) = results.next().await {
        println!("{:?}", row.get::<String>("p.name")?);
    }

    // Transaction
    let tx = client.begin().await?;
    tx.execute("CREATE (p:Person {name: $name})", params! { "name" => "Bob" }).await?;
    tx.commit().await?;

    Ok(())
}

Zig Client

Usage (client is vendored in geode-client-zig/):

const geode = @import("geode_client");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    var client = try geode.Client.connect(allocator, "localhost", 3141);
    defer client.disconnect();

    // Execute query
    const result = try client.execute("MATCH (p:Person) RETURN p.name", .{});
    defer result.deinit();

    // Process results
    while (try result.next()) |row| {
        const name = try row.get("p.name");
        std.debug.print("Name: {s}\n", .{name});
    }
}

API Reference

Connection Management

Python:

client = Client(
    address="localhost:3141",
    tls_verify=True,
    pool_size=100,
    pool_timeout=30,
    query_timeout=60
)

Go:

db, err := sql.Open("geode", "quic://user:pass@localhost:3141?tls=true")

Rust:

let client = Client::builder()
    .address("localhost:3141")
    .pool_size(100)
    .connect_timeout(Duration::from_secs(30))
    .connect()
    .await?;

Transaction API

Python:

async with client.connection() as tx:
    await tx.begin()
    await tx.execute(query1)

    sp = await tx.savepoint("checkpoint")
    try:
        await tx.execute(query2)
    except:
        await tx.rollback_to(sp)

    await tx.execute(query3)

Go:

tx, _ := db.Begin()
tx.Exec(query1)
tx.Exec(query2)
tx.Commit()  // or tx.Rollback()

Rust:

let tx = client.begin().await?;
tx.execute(query1, params).await?;
tx.execute(query2, params).await?;
tx.commit().await?;

Result Handling

Python:

result, _ = await client.query(query)

# Get first row
row = await result.first()

# Iterate all rows
for row in result.rows:
    value = row['column_name']

# Get all rows as list
rows = await result.collect()

Go:

rows, _ := db.Query(query)
defer rows.Close()

for rows.Next() {
    var name string
    var age int
    rows.Scan(&name, &age)
}

Rust:

let mut stream = client.execute(query, params).stream().await?;

while let Some(row) = stream.next().await {
    let name: String = row.get("name")?;
    let age: i64 = row.get("age")?;
}

Best Practices

Query Optimization

Use Parameters:

# Good: Prepared statement, safe from injection
result, _ = await client.query(
    "MATCH (p:Person {name: $name}) RETURN p",
    name=user_input
)

# Bad: String concatenation, vulnerable
query = f"MATCH (p:Person {{name: '{user_input}'}}) RETURN p"

Create Indexes:

CREATE INDEX ON Person(email)
CREATE INDEX ON Product(name)

Profile Queries:

PROFILE MATCH (a:Person)-[:KNOWS*2]->(b)
WHERE a.city = 'NYC'
RETURN b.name

Transaction Management

Keep Transactions Short:

# Good: focused transaction
async with client.connection() as tx:
    await tx.begin()
    await tx.execute("CREATE (p:Person {name: 'Alice'})")

# Bad: long-running transaction holding locks
async with client.connection() as tx:
    await tx.begin()
    for item in huge_list:  # Don't do this
        await tx.execute(f"CREATE (p:Person {{name: '{item}'}})")

Use Savepoints for Complex Operations:

async with client.connection() as tx:
    await tx.begin()
    await tx.execute(critical_operation)

    sp = await tx.savepoint("optional_part")
    try:
        await tx.execute(optional_operation)
    except:
        await tx.rollback_to(sp)  # Keep critical_operation

Error Handling

Implement Retry Logic:

from tenacity import retry, stop_after_attempt, wait_exponential

@retry(
    stop=stop_after_attempt(3),
    wait=wait_exponential(multiplier=1, min=1, max=10)
)
async def execute_with_retry(client, query):
    result, _ = await client.query(query)
    return result

Handle Specific Errors:

from geode_client import QueryError

try:
    await client.execute(query)
except QueryError as exc:
    message = str(exc).lower()
    if "40502" in message:
        # Retry this one
        pass
    elif "syntax" in message:
        # Don't retry syntax errors
        logger.error("Invalid query syntax")
        raise
    else:
        raise

Production Deployment

Configuration

Server Configuration:

server:
  listen: 0.0.0.0:3141
  max_connections: 10000
  tls:
    cert_file: /etc/geode/server.crt
    key_file: /etc/geode/server.key

storage:
  data_dir: /var/lib/geode
  wal_dir: /var/lib/geode/wal

performance:
  query_timeout: 60s
  transaction_timeout: 300s
  max_query_memory: 1GB

Client Configuration:

client = Client(
    address="geode.example.com:3141",
    tls_verify=True,
    pool_size=100,
    connect_timeout=30,
    query_timeout=60
)

Monitoring

Key Metrics:

  • Query latency (p50, p95, p99)
  • Transaction throughput
  • Connection pool utilization
  • Error rates
  • Memory usage

Health Checks:

@app.get("/health")
async def health():
    try:
        await client.execute("MATCH (n) RETURN count(n) LIMIT 1")
        return {"status": "healthy"}
    except:
        return {"status": "unhealthy"}, 503

Backup & Recovery

Backup Strategy:

# Snapshot backup
./geode backup --output /backups/geode-$(date +%Y%m%d).tar.gz

# Continuous WAL archiving
./geode wal-archive --destination s3://backups/wal/

Recovery:

# Restore from snapshot
./geode restore --input /backups/geode-20250124.tar.gz

# Point-in-time recovery
./geode restore --snapshot /backups/base.tar.gz --wal /backups/wal/ --target-time 2025-01-24T15:30:00Z
  • Tutorials: Step-by-step learning guides
  • Examples: Working code samples
  • API Reference: Complete function documentation
  • Performance Guide: Optimization techniques
  • Security Guide: Authentication and authorization

Related Articles