TypeDB 3.0 is live! Get started for free.
TypeDB enables software engineers to build data applications faster, with a modern language that avoids complexity.
match$userisa user,has full-name$name,has email$email;fetch$name;$email;# This returns all users of any typematch$userisa employee,has full-name$name,has email$email,has employee-id$id;fetch$name;$email;$id;# This returns only users who are employeesmatch$user-typesub user;$userisa$user-type,has full-name$name,has email$email;fetch$name;$email;$user-type;# This returns all users and their type
Every data point has a type that you define, likeemployee
. Types can also be subtypes of other types, likeuser
. So, a query foruser
entities could return bothemployee
andcontractor
entities, without the query having to specify this. We call this polymorphism, and it drastically simplifies how you interact with your database.
insert$johnisa full-time-employee,has full-name"John Doe",has primary-email"john.doe@typedb.com",has email"j.doe@typedb.com",has email"john@typedb.com",has employee-id183;$readmeisa file,has path"/home/johndoe/repos/typedb/readme.md";$editisa action,has name"edit file";$kevinisa user,has email"kevin@typedb.com";$perm(subject:$john, object:$readme, action:$edit)isa permission;$rqst(target:$perm, requestee:$kevin)isa change-request,has requested-change"revoke";
Types, attributes and relations aren’t things you have to implement: they’re core features. With the freedom to model hierarchies, multi-valued attributes, n-ary relations, nested relations and more, the conceptual data modelis the data model. This makes database design, and writing queries, incredibly intuitive.
define usersubentity,owns full-name,owns email@unique; employeesub user,owns employee-id@key,plays team-membership:member; membershipsubrelation,abstract,relates parent,relates member; team-membershipsub membership,relates teamas parent; full-namesubattribute,valuestring; idsubattribute,valuestring; emailsub id; employee-idsub id;
TypeDB ensures conceptual consistency between your type hierarchies and declared type behaviours. This allows the schema to be trivially extended with no migrations or downtime. You can create abstract types and override supertype properties within the schema, bringing the expressive power of OOP to the database layer.
match$userisa user;fetch$user: full-name;match$userisa user,has email"john@typedb.com";fetch$user: full-name;match$userisa user,has email"john@typedb.com";(subject:$user, object:$tql, action:$action)isa permission;$tqlisa file,has extension"tql";fetch$user: full-name;match$userisa user;(subject:$user, object:$tql, action:$action)isa permission;$tqlisa file,has extension"tql";(team:$eng, member:$user)isa team-membership;$engisa team,has name"Engineering";fetch$user: full-name;
TypeQL works through declarative pattern matching, allowing queries to be scoped to any level in the type hierarchy without considering physical data stores or execution strategy. As a composable language, concatenating two valid patterns will always produce a new valid one. Thanks to the high level of abstraction and near-natural syntax, domain experts can instantly understand a query's intent even with no knowledge of TypeQL.
define weekly-hourssubattributevaluelong; full-time-employeesub employee; part-time-employeesub employee,owns weekly-hours;insert$francoisisa full-time-employee,has full-name"François Durand",has email"francois@typedb.com",has employee-id184,has weekly-hours35;# [THW03] Invalid Write: Attribute of type 'weekly-hours' is# not defined to be owned by type 'full-time-employee'.
With declarative schemas backed by static type checking, all queries are validated to ensure nonsensical writes are automatically blocked, and nonsensical reads throw an exception instead of returning an empty result set. Sessions and transactions are also strongly typed, preventing unauthorized and unintentional modifications from committing.
Designing your database requires nothing more than describing the real things your data represents. This puts an end to ‘object-relational mismatch’. You no longer need to think about your data in a different way between your application and your database.
insert$johnisa full-time-employee,has full-name"John Doe",has primary-email"john.doe@typedb.com",has email"j.doe@typedb.com",has email"john@typedb.com",has employee-id183;$readmeisa file,has path"/home/johndoe/repos/typedb/readme.md";$editisa action,has name"edit file";$kevinisa user,has email"kevin@typedb.com";$perm(subject:$john, object:$readme, action:$edit)isa permission;$rqst(target:$perm, requestee:$kevin)isa change-request,has requested-change"revoke";
TypeDB uses the Enhanced Entity-Relationship model with a declarative schema and static type checking. This allows the natural implementation of a type hierarchy, multivalued attributes, and n-ary and nested relations. Leverage OOP concepts like abstraction, inheritance, and polymorphism without warping the conceptual model. Normalization, null values, and reification are things of the past.
DO $$DECLARE inserted_user_idINTEGER; inserted_resource_idINTEGER; inserted_action_idINTEGER; inserted_requestee_idINTEGER; inserted_permission_idINTEGER;BEGININSERTINTO users(id, full_name)VALUES(DEFAULT,'John Doe')RETURNING idINTO inserted_user_id;INSERTINTO user_emails(user_id, email, is_primary)VALUES(inserted_user_id,'john.doe@typedb.com',TRUE),(inserted_user_id,'j.doe@typedb.com',FALSE),(inserted_user_id,'john@typedb.com',FALSE);INSERTINTO employees(user_id, employee_id)VALUES(inserted_user_id,183);INSERTINTO full_time_employees(user_id)VALUES(inserted_user_id);INSERTINTO resources(id)VALUES(DEFAULT)RETURNING idINTO inserted_resource_id;INSERTINTO files(inserted_resource_id, path)VALUES(inserted_resource_id,'/home/johndoe/repos/typedb/readme.md');INSERTINTO actions(id, name)VALUES(DEFAULT,'edit file')RETURNING idINTO inserted_action_id;INSERTINTO users(id, full_name)VALUES(DEFAULT,NULL)RETURNING idINTO inserted_requestee_id;INSERTINTO user_emails(user_id, email, is_primary)VALUES(inserted_requestee_id,'kevin@typedb.com',FALSE);INSERTINTO permissions(id, subject, object,action)VALUES(DEFAULT, inserted_user_id, inserted_resource_id, inserted_action_id)RETURNING idINTO inserted_permission_id;INSERTINTO change_requests(id, target, requestee, requested_change)VALUES(DEFAULT, inserted_permission_id, inserted_requestee_id,'revoke');COMMIT;END $$;
SQL was designed in a pre-OOP era. Schemas and queries quickly grow out of control when implementing class-table inheritance and normalizing multivalued attributes. Inheritance is also impossible to implement without losing referential integrity. This can only be fixed with brittle constraint-defining syntax that requires continual maintenance or integrity control in the application layer.
db.change_requests.aggregate([{ $lookup:{ from:"permissions", localField:"target", foreignField:"_id",as:"permission"}},{ $unwind:{ path:"$permission"}},{ $lookup:{ from:"users", localField:"user", foreignField:"_id",as:"user"}},{ $unwind:{ path:"$user"}},{ $lookup:{ from:"users", localField:"permission.user", foreignField:"_id",as:"permission.user"}},{ $unwind:{ path:"$permission.user"}},{ $lookup:{ from:"identifiers", localField:"permission.object", foreignField:"_id",as:"permission.object"}},{ $unwind:{ path:"$permission.object"}},{ $lookup:{ from:"actions", localField:"permission.action", foreignField:"_id",as:"permission.action"}},{ $unwind:{ path:"$permission.action"}},{ $project:{ _id:false, user:"$permission.user.email", requestee:"$user.email", target:"$permission.object.identifier", action:"$permission.action.name", request:"$req_change"}}])
Document databases are designed and optimized to store hierarchical data. They can easily store multivalued attributes as list fields and null values as the absence of fields. However, inserting and retrieving highly interconnected data is complicated and poorly optimized. All similarly shaped data is dumped into the same collection without a schema, leaving the backend developer responsible for maintaining integrity and structure.
MATCH(john:User),(readme:File{path:"/home/johndoe/repos/typedb/readme.md"}),(edit:Action{name:"edit file"})WHERE( john.primary_email="john@typedb.com"OR"john@typedb.com"IN john.alias_emails)ANDNOTEXISTS{MATCH(john)<-[:SUBJECT]-(perm:Permission)-[:OBJECT]->(readme)WHEREEXISTS((perm)-[:ACTION]->(edit))}WITH john, readme, editCREATE(john)<-[:SUBJECT]-(perm:Permission)-[:OBJECT]->(readme)WITH edit, permCREATE(perm)-[:ACTION]->(edit);MATCH(perm:Permission),(perm)-[:SUBJECT]->(john:User),(perm)-[:OBJECT]->(readme:File{id:"/home/typedb/repos/typedb/readme.md"}),(perm)-[:ACTION]->(edit:Action{name:"edit file"}),(kevin:User)WHERE( john.primary_email="john@typedb.com"OR"john@typedb.com"IN john.alias_emails)AND( kevin.primary_email="kevin@typedb.com"OR"kevin@typedb.com"IN kevin.alias_emails)CREATE(rqst:ChangeRequest{requested_change:"revoke"}),(rqst)-[:TARGET]->(perm),(rsqt)-[:REQUESTEE]->(kevin);
Graph databases, despite excelling at storing traditional graph-like data, cannot natively express relations other than strictly binary ones between two entities. This forces you to reify the model: turn relationships from edges into nodes, blurring the lines between entity and relation. Polymorphism is challenging to handle without a proper schema, forcing type hierarchies to be implemented as structureless data labels.
Extending an SQL database often involves a lengthy migration process. With TypeDB, you can extend your model without refactoring your queries or application code. Everything still works, because the data logic is in the structure, not the query.
match$userisa user,has email$email;$rsrcisa!$resource-type,has id$id;$resource-typesub resource;$own(resource:$rsrc, owner:$user)isa resource-ownership;fetch$email;$resource-type;$id;
TypeDB is a truly polymorphic database. Queries are written in a high-level declarative language and resolved against the schema at query-time. Variables implicitly match all valid types, so queries never have to be updated when new subtypes are added. Attributes and relations are always implemented in the same way, so cardinality changes never require a change to the underlying model. All this means that extensions to the data model are trivial, and don't require refactors.
SELECT ue.emailAS email,'file'AS resource_type, f.pathAS idFROM users uJOIN user_emails ueON u.id= ue.user_idJOIN resource_ownerships roON u.id= ro.user_idJOIN resources rON ro.resource_id= r.idJOIN files fON r.id= f.resource_idUNIONALLSELECT ue.emailAS email,'directory'AS resource_type, d.pathAS idFROM users uJOIN user_emails ueON u.id= ue.user_idJOIN resource_ownerships roON u.id= ro.user_idJOIN resources rON ro.resource_id= r.idJOIN directories dON r.id= d.resource_idUNIONALLSELECT ue.emailAS email,'commit'AS resource_type, c.hashAS idFROM users uJOIN user_emails ueON u.id= ue.user_idJOIN resource_ownerships roON u.id= ro.user_idJOIN resources rON ro.resource_id= r.idJOIN commits cON r.id= c.resource_idUNIONALLSELECT ue.emailAS email,'repository'AS resource_type, re.nameAS idFROM users uJOIN user_emails ueON u.id= ue.user_idJOIN resource_ownerships roON u.id= ro.user_idJOIN resources rON ro.resource_id= r.idJOIN repositories reON r.id= re.resource_idUNIONALLSELECT ue.emailAS email,'table'AS resource_type, t.nameAS idFROM users uJOIN user_emails ueON u.id= ue.user_idJOIN resource_ownerships roON u.id= ro.user_idJOIN resources rON ro.resource_id= r.idJOINtables tON r.id= t.resource_idUNIONALLSELECT ue.emailAS email,'database'AS resource_type, f.nameAS idFROM users uJOIN user_emails ueON u.id= ue.user_idJOIN resource_ownerships roON u.id= ro.user_idJOIN resources rON ro.resource_id= r.idJOINdatabases dON r.id= d.resource_id;
SQL's inability to natively model inheritance and polymorphism leads to brittle queries that must explicitly describe all data to be returned. When the data model changes, many queries cease to be valid, requiring extensive modifications to your application code. Not only does data need to be carefully migrated to maintain integrity, but the migration process extends outside of the database to any application components that need to interact with it.
db.resource_ownerships.aggregate([{ $lookup:{ from:"resources", localField:"resource", foreignField:"_id",as:"resource"}},{ $unwind:{ path:"$resource"}},{ $lookup:{ from:"users", localField:"owner", foreignField:"_id",as:"owner"}},{ $unwind:{ path:"$owner"}},{ $unwind:{ path:"$owner.emails"}},{ $addFields:{ resource_id:{ $switch:{ branches:[{case:{ $eq:["$resource.resource_type","file"]}, then:"$resource.path"},{case:{ $eq:["$resource.resource_type","directory"]}, then:"$resource.path"},{case:{ $eq:["$resource.resource_type","commit"]}, then:"$resource.hash"},{case:{ $eq:["$resource.resource_type","repository"]}, then:"$resource.name"},{case:{ $eq:["$resource.resource_type","table"]}, then:"$resource.name"},{case:{ $eq:["$resource.resource_type","database"]}, then:"$resource.name"}]}}}},{ $project:{ _id:false, email:"$owner.emails", resource_type:"$resource.resource_type", id:"$resource_id"}}])
With document databases, the freeform structure makes inserting data trivial, but writing and validating queries a significant challenge. Migrating data relies on complete knowledge of its layout to ensure that no errors are introduced. When the layout of the data changes, any queries or code that rely on the previous structure stop working. The complex and brittle aggregation pipelines are particularly difficult to maintain and troubleshoot, especially when utilising lookups across multiple collections.
MATCH(user:User)-[:OWNS]->(rsrc:Resource)WITH rsrc,[user.primary_email]+ user.alias_emailsAS emails,labels(rsrc)AS resource_types,keys(rsrc)AS propertiesUNWIND emailsAS emailUNWIND resource_typesAS resource_typeWITH rsrc, email, resource_type, properties,{ File:"path", Directory:"path",Commit:"hash", Repository:"name", Table:"name", Database:"name"}AS id_type_mapWHERE resource_typeINkeys(id_type_map)AND id_type_map[resource_type]IN propertiesRETURN email, resource_type, rsrc[id_type_map[resource_type]]AS id
In graph databases, properties are attached directly to the nodes that own them rather than being nodes themselves. Relocating attributes between nodes or reifying relations causes breaking changes to queries that rely on them. These changes to the logical data model and queries must be manually carried out due to the lack of a schema to define structure. Those model discrepancies can lead to data inconsistency and integrity issues.
TypeDB has the performance to resolve even complex queries in milliseconds. So you never need to precompute results or store redundant copies, which can result in stale, inconsistent and incorrect results. Work with a single source of truth, all the time.
definerule transitive-team-membership:when{(team:$team-1, member:$team-2)isa team-membership;(team:$team-2, member:$member)isa team-membership;}then{(team:$team-1, member:$member)isa team-membership;};match$userisa user,has email$email;$teamisa team,has name$name;(team:$team, member:$user)isa team-membership;fetch$email;$name;
TypeDB's built-in reasoning engine allows rules to be defined in the schema and resolved at query time. Rules are constructed using first-order logic and define how new facts can be inferred from existing data. When a query is issued, matching rules are triggered using the most recent data available. This allows all computed data to be built on a single source of truth, ensuring accuracy and consistency without delay or redundancy.
CREATEPROCEDURE reload_inherited_team_memberships()LANGUAGESQLBEGIN ATOMICTRUNCATETABLE inherited_team_memberships;WITH RECURSIVE parent_teams(team, member)AS(SELECT team_memberships.team, team_memberships.memberFROM team_membershipsWHERE team_memberships.memberIN(SELECT idFROMuser)UNIONALLSELECT team_memberships.team, team_memberships.memberFROM team_membershipsJOIN parent_teamsON parent_teams.team= team_memberships.member)INSERTINTO inherited_team_memberships(team, member)SELECT team, memberFROM parent_teams;COMMIT;END;SELECT users.emailAS email, teams.nameAS nameFROM inherited_team_membershipsJOIN usersON users.id= inherited_team_memberships.memberJOIN teamsON teams.id= inherited_team_memberships.team;
SQL's stored procedures enable computed data to be generated periodically. As modern data has grown in complexity, they have ceased to be performant enough for real-time use. Stale data accumulates as updates aren't immediately reflected, creating inconsistencies between the source and derived data. Scripts calling multiple procedures can even leave the data in an incorrect state due to propagating errors or race conditions.
inherited_team_memberships= db.team_memberships.aggregate([{ $lookup:{ from:"users", localField:"member", foreignField:"_id",as:"member"}},{ $unwind:{ path:"$member"}},{ $graphLookup:{ from:"team_memberships", startWith:"$team", connectFromField:"team", connectToField:"member",as:"parent_teams"}},{ $project:{ _id:false, email:"$member.email", team:{ $setUnion:[["$team"],"$parent_teams.team"]}}},{ $unwind:{ path:"$team"}},{ $lookup:{ from:"teams", localField:"team", foreignField:"_id",as:"team"}},{ $unwind:{ path:"$team"}},{ $project:{ email:"$email", name:"$team.name"}}])db.inherited_team_memerships.remove({})db.inherited_team_memberships.insertMany(inherited_team_memberships)db.inherited_team_memberships.find({})
Document databases struggle to query efficiently when foreign document references are used to connect multiple collections. Lookup functions in aggregation pipelines require developers to explicitly identify and traverse every possible path between documents, forming a long and brittle pipeline. Missing or dissimilar fields in input documents silently generate bad data, requiring the pipeline to be painstakingly debugged stage-by-stage.
MATCH()<-[sub:SUBJECT]-(perm:InheritedPermission)-[obj:OBJECT]->(),(perm)-[act:ACTION]->()DELETE sub, obj, act, perm;MATCH(perm:Permission),(user:User),(rsrc:Resource),(actn:Action)WHERE((perm)-[:SUBJECT]->(user:User)OR(perm)-[:SUBJECT]->(:Team)<-[:MEMBER_OF*]-(user:User))AND((perm)-[:OBJECT]->(rsrc:Resource)OR(perm)-[:OBJECT]->(:Collection)<-[:MEMBER_OF*]-(rsrc:Resource))AND((perm)-[:ACTION]->(actn:Action)OR(perm)-[:ACTION]->(:ActionSet)<-[:MEMBER_OF*]-(actn:Action))ANDNOTEXISTS{MATCH(user)<-[:SUBJECT]-(perm:InheritedPermission)-[:OBJECT]->(rsrc)WHEREEXISTS((perm)-[:ACTION]->(actn))}WITH user, rsrc, actnCREATE(user)<-[:SUBJECT]-(perm:InheritedPermission)-[:OBJECT]->(rsrc)WITH actn, permCREATE(perm)-[:ACTION]->(actn);MATCH(user:User)<-[:SUBJECT]-(perm:InheritedPermission)-[:OBJECT]->(rsrc:Resource),(perm)-[:ACTION]->(actn:Action)RETURN user, rsrc, actn;
Neo4j excels at identifying simple transitive relationships, but branching patterns are harder to express. This forces developers to specify all potential combinations of nodes, relationships, and properties, resulting in unmaintainable queries and poor performance. Because Neo4j lacks the expressive power of symbolic reasoning, these queries must be periodically executed to precompute the results, leading to stale and inconsistent data.
We provide and maintain a whole ecosystem of reliable tools and documentation that make developing with TypeDB a joy.
Run in a local Docker container or on AWS, GCP, and Azure with TypeDB Cloud, no dependencies/config needed.
Manage databases, build and execute queries, and explore results with TypeDB Studio, a cross-platform IDE.
Build complex and high-volume transactional applications with reactive and resilient open-source clients.
We provide and officially support native drivers for all the languages below. Our APIs are asynchronous, reactive, stateful, and programmatic.
goget github.com/typedb/typedb-driver/go
TypeDB Cloud is a fully-managed cloud database for your application, starting at $0/month with generous limits. Once your application needs it, upgrade for enterprise-level availability, auto-scaling, security and access control.
Global deployment, cloud agnosticDeploy in regions across AWS, GCP, and Azure, with different databases in different cloud providers and regions. | |
Scalability on demand, native clusteringConnect to clusters with topology-aware clients which can load balance requests across multiple replicas within a cluster. | |
Secure by default, roles and encryptionEnsure a high level of protection to safeguard data and maintain confidentiality with fully secured by default, with authentication as well as storage and network encryption. | |
Always available, automatic failoverGuarantee constant access to data thanks to synchronous replication and replicas which will automatically failover if the primary fails. | |
Teams and projects, organizational governanceCreate teams, members and projects to manage databases across the entire organization with ease. |
The TypeDB community is vibrant, active and always helpful. Join us on Discord or make contributions on GitHub. New faces are always welcome.
Discord
YouTube
With its simple yet immensely powerful query language, native support for N-ary relationships and focus on semantic schema, TypeDB solves all our modeling problems so that we can focus more on solving higher level problems instead of tweaking traditional graph databases to fit our use cases.
Ram Anvesh
Software Engineer, FlipkartTypeDB enables us to model the supply chains of large Global CPG clients and identify environmental and social risks. We have found its strongly typed nature and expressivity to be world leading, allowing our customers to gain novel insights. With TypeDB, we can solve problems at the world's greatest companies.
Dixit Shah
Managing Consultant, IBMFor developers, TypeDB is really easy to work with. Its unique and expressive type system enables us to spend less time data modeling. We can easily integrate complex biomedical datasets. TypeDB provides us the backbone to our therapeutics platform to cure neurodegenerative diseases.
Nik Sharma
Founder & CEO, BioCorteXTypeDB's expressivity allows us to unify all levels of cyber intelligence for cyber security knowledge management systems. Through nested and hyper relations, we can easily represent TTPs and observables, attribution and victimology. During an attack, this gives analysts 360 views of any observable.
Samuel Hassine
Co-founder & CEO, FiligranTypeDB is a powerful framework for data exploration. The way data, attributes and relations can be expressed in a polymorphic manner allows us to build a rich network of multi-levels analysis and open opportunities to query, discover and infer interactions.
Jean-Paul Mochet
Chief Enterprise Architect, CapgeminiTypeDB provides a strongly-typed database with N-ary relations that enables modeling the world much closer to reality compared to other databases. In addition, its built-in inference engine enables to build next generation AI systems — one of the many reasons to choose TypeDB to model biomedical data.
Konrad Myśliwiec
Data Scientist Engineer, RocheTypeDB makes it easy for our robots to operate autonomously in the real world by being the centre of their understanding. TypeDB makes it easy to incorporate expert knowledge and advanced reasoning into its knowledge base.
Joris Sijs
System Architect, AvularCloud or container, a polymorphic database with a conceptual data model, a strong subtyping system, a symbolic reasoning engine, and a type-theoretic language is minutes away.