TypeDB vs Postgres

When your data stops looking like tables

Postgres stores your data and leaves its meaning to the joins and code around it. TypeDB stores the meaning too, which matters most for connected, graph-shaped domains.

Get TypeDB open source
TypeDB
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
match
  $b isa company, has name "Acme Ltd";
  $l isa loan, links (borrower: $b,
                      guarantor: $g), has amount $a;
fetch { "guarantor": $g.name, "amount": $a };
Postgres
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
SELECT g.name, l.amount
FROM loan l
JOIN loan_party b  ON b.loan_id = l.id AND b.role = 'borrower'
JOIN company bc    ON bc.id = b.company_id
JOIN loan_party gp ON gp.loan_id = l.id AND gp.role = 'guarantor'
JOIN company g     ON g.id = gp.company_id
WHERE bc.name = 'Acme Ltd';

Overview

Main differences from Postgres

We typically see businesses choose TypeDB when they have large, messy domains, when they are dealing with critical systems that require accuracy, or when they want their data architecture to give them an unfair AI advantage.

Relations, not join tables

A loan with a lender, borrower and guarantor is one relation in TypeDB, not a join table with a role column.

Many values, declared

Things that come in varying numbers are part of the model, with no extra tables and no rows multiplying in joins.

Queries checked for meaning

Postgres checks names and types. TypeDB also checks roles and relationships, so a wrong query fails.

Relationships

Join tables, or relations with roles

Postgres can model a loan with three parties. It takes a join table, a role column and a join for every participant.

TypeDB

The loan is one relation with three named roles. Queries name the roles directly, and the schema says which types can play them.

Postgres

A loan_party table with a role column. Every query rebuilds the relationship with one join per participant, and the roles are just strings.

Cardinality

When one thing has many answers

A person has several email addresses. A deal has any number of advisors. Real domains are full of values that come in varying numbers.

TypeDB
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
entity person,
  owns email @card(1..);

relation deal,
  relates buyer @card(1),
  relates advisor @card(0..);

How many values are allowed is declared in the schema and checked on every write, so a deal without a buyer never gets stored.

Postgres
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
SELECT d.id, SUM(f.amount)
FROM deal d
JOIN deal_advisor a ON ...
JOIN deal_fee f     ON ...
GROUP BY d.id;
-- each fee counted once per advisor

Each multi-valued field becomes its own table. Join two of them and rows multiply, so sums inflate unless every query guards against it.

Catching mistakes

Valid SQL can still mean the wrong thing

Postgres is strict about tables, columns and types, and enforces foreign keys. What it can't check is whether a query means what you intended.

TypeDBPostgres
Misspelt column or table Error Error
Misspelt role, such as 'guarenter' Error: the role isn't defined Runs, returns no rows: it's just a string
Join on the wrong key of the same type No join keys to get wrong; roles are checked Runs, returns plausible wrong rows
93%of incorrect TypeQL queries came back as an error you could act on
5%of incorrect SQL queries did the same (on MySQL); the rest failed silently

First-attempt queries by Claude Sonnet 5, same dataset, counting only the incorrect ones. SQL was more often right first time, 82.1% against 75.2%; with retries TypeQL finished ahead with both models tested. See the benchmark →

Hierarchies

Categories within categories

Parties that can be companies or people. Companies that can be banks or funds. Relational models can handle it, at a cost.

TypeDB
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
entity party;
entity company sub party;
entity bank sub company;

# One query returns every kind of party
match $p isa party;

Subtypes inherit attributes and roles. One query returns every kind of party, including ones you add later, without a migration.

Postgres
  • One wide table with a type column and many nullable fields
  • A table per subtype, with UNIONs to query them together
  • Table inheritance, where indexes and foreign keys don't carry over

Adding a subtype usually means a migration and finding every query that joins through it.

Use Cases

What teams build on TypeDB

01 · Agent grounding

Give agents a model of your domain they can check their work against.

  • Agents query the same typed model your application uses
  • Wrong queries come back as errors the agent can fix
  • Connect through MCP and a TypeDB skill
match
  $c isa customer, has name "Acme";
  $p isa policy, links (holder: $c);
fetch { "policy": $p.ref };

Which to choose

Pick the one that fits the problem

Choose TypeDB when

  • your schema is mostly join tables and role columns
  • your data has values that come in varying numbers
  • your domain has deep hierarchies that keep growing
  • agents or analysts write queries and wrong ones need to fail loudly

Choose Postgres when

  • your data is mostly tabular and relationships are simple
  • you need a transactional system of record with a vast ecosystem
  • your team lives in SQL and the joins aren't getting in the way

Or use both

Plenty of teams keep Postgres as the system of record and load the connected, graph-shaped part of their domain into TypeDB for the queries that need it.

Reference

The full comparison

TypeDBPostgres
What it stores Data and its meaning: types, roles and what's allowed Data; meaning lives in joins and application code
Data model Entities, relations and attributes, organised into types Tables, rows and columns
Many-sided relationships One relation, each participant in a named role A join table with a role column
Multiple values Native, with cardinality declared and enforced A table per multi-valued field; joins multiply rows
Hierarchies Native inheritance, with polymorphic queries Wide tables, table-per-subtype or table inheritance
Schema Required, and enforced on every write Required, and enforced on every write
Bad queries Checked against the domain model, including roles Checked for names and types
Best for Connected, complex domains General-purpose transactional and analytical work

Move your graph-shaped data off Postgres.
Relations, roles and types in one database.

Get TypeDB open sourceTalk to us
Feedback