3 ms·
Show HN: We built a type-safe Python ORM for RedisGraph/FalkorDB
We were tired of writing raw Cypher — escaping quotes, zero autocomplete, refactoring nightmares — so we built GraphORM: a type-safe Python ORM for RedisGraph/FalkorDB using pure Python objects.
What it does
Instead of fragile Cypher:
query = """
MATCH (a:User {user_id: 1})-[r1:FRIEND]->(b:User)-[r2:FRIEND]->(c:User)
WHERE c.user_id <> 1 AND b.active = true
WITH b, count(r2) as friend_count
WHERE friend_count > 5
RETURN c, friend_count
ORDER BY friend_count DESC
LIMIT 10
"""
You write type-safe Python:
stmt = select().match(
(UserA, FRIEND.alias("r1"), UserB),
(UserB, FRIEND.alias("r2"), UserC)
).where(
(UserA.user_id == 1) & (UserC.user_id != 1) & (UserB.active == True)
).with_(
UserB, count(FRIEND.alias("r2")).label("friend_count")
).where(
count(FRIEND.alias("r2")) > 5
).returns(
UserC, count(FRIEND.alias("r2")).label("friend_count")
).orderby(
count(FRIEND.alias("r2")).desc()
).limit(10)
Key features:
• Type-safe schema with Python type hints
• Fluent query builder (select().match().where().returns())
• Automatic batching (flush(batch_size=1000))
• Atomic transactions (with graph.transaction(): ...)
• Zero string escaping — O'Connor and "The Builder" just work
Target audience
• AI/LLM agent devs: store long-term memory as graphs (User → Message → ToolCall)
• Web crawler engineers: insert 10k pages + links in 12 lines vs 80 lines of Cypher
• Social network builders: query "friends of friends" with indegree()/outdegree()
• Data engineers: track lineage (Dataset → Transform → Output)
• Python devs new to graphs: avoid Cypher learning curve
Data insertion: the real game-changer
Raw Cypher nightmare:
queries = [
"""CREATE (:User {email: "alice@example.com", name: "Alice O\\'Connor"})""",
"""CREATE (:User {email: "bob@example.com", name: "Bob \\"The Builder\\""})"""
]
for q in queries:
graph.query(q) # No transaction safety!
GraphORM bliss:
alice = User(email="alice@example.com", name="Alice O'Connor")
bob = User(email="bob@example.com", name='Bob "The Builder"')
graph.add_node(alice)
graph.add_edge(Follows(alice, bob, since=1704067200))
graph.flush() # One network call, atomic transaction
Try it in 30 seconds
pip install graphorm
from graphorm import Node, Edge, Graph
class User(Node):
__primary_key__ = ["email"]
email: str
name: str
class Follows(Edge):
since: int
graph = Graph("social", host="localhost", port=6379)
graph.create()
alice = User(email="alice@example.com", name="Alice")
bob = User(email="bob@example.com", name="Bob")
graph.add_node(alice)
graph.add_edge(Follows(alice, bob, since=1704067200))
graph.flush()
GitHub: https://github.com/hello-tmst/graphorm https://github.com/hello-tmst/graphorm
We'd love honest feedback:
• Does this solve a real pain point for you?
• What's missing for production use?
• Any API design suggestions?
- justinlords 8mo agoThis looks solid, the type safety and automatic escaping alone would've saved me hours debugging Cypher strings. The batching is smart too. Curious how it handles variable-length paths though, since that's where most ORMs get messy. The AI agent memory use case makes a lot of sense.
- hello-tmst 8mo agoGreat question — variable-length paths are fully supported in the query builder: # Friends of friends (1 to 3 hops) stmt = select().match( (User.alias("a"), FRIEND.variable_length(1, 3), User.alias("b")) ).where( User.alias("a").user_id == 1 ).returns( User.alias("b") ) Generates: `(a)-[:FRIEND1..3]->(b)` *Variants:* - Unbounded: `FRIEND.variable_length()` → `` - Exact length: `FRIEND.variable_length(2, 2)` → `2` - Min only: `FRIEND.variable_length(1)` → `1..` - Range: `FRIEND.variable_length(1, 3)` → `*1..3` You can also use the explicit `VariableLength(FRIEND, 1, 3)` constructor if you need to alias the path or reference it later in the query. The builder handles all edge cases (empty paths, cycles) the same way RedisGraph does — we compile to idiomatic Cypher without abstraction leaks. Raw string patterns still work if you need something exotic.
- shahar_biron 8mo agoBTW, in falkordb we also developed an ORM library, also covering RBAC based security features. https://github.com/FalkorDB/falkordb-py-orm https://github.com/FalkorDB/falkordb-py-orm
- hello-tmst 8mo agoThanks for sharing these resources — great to see the FalkorDB ecosystem growing! We built GraphORM with a focus on type safety + fluent query composition (e.g., indegree()/outdegree() helpers, chainable .match().where().returns()), which we found missing in existing RedisGraph/FalkorDB Python tooling. Happy to see multiple approaches emerging — diversity helps the ecosystem. We're also planning to expand support beyond RedisGraph/FalkorDB to other Cypher-compatible databases where the query model fits. Would love to exchange ideas on ORM design patterns if you're open to it.