EdgeDB

The next generation object database.

Open source, simple to use, batteries included.

Public Technology Preview coming soon.

Designed for the Future

EdgeDB is designed to support modern and emerging approaches to building highly reactive and flexible applications.

Thanks to its elegant data model and expressive query language, EdgeDB greatly simplifies working with complex application data.

Robust Architecture

EdgeDB has been in active development for more than 7 years, and relied upon in production by large businesses.

EdgeDB is built on top of PostgreSQL, inheriting all its core strengths: ACID compliance, performance, tooling, and reliability.

EdgeQL and GraphQL

EdgeQL is the default language of EdgeDB. Conceptually it is a superset of GraphQL, which will be supported directly as well.

EdgeQL supports fetching object hierarchies with arbitrary level of nesting, filtering, sorting, aggregation, window functions, and recursion.

EdgeDB data model is a semantic network. At its core there are two fundamental notions: concepts and relationships. Both can be defined as a hierarchical composition of other concepts and relationships, enabling powerful reuse patterns and intuitive representation of real-world knowledge.

Plug and Play

EdgeDB supports a high-performance protocol for language bindings, as well as a straightforward REST API.

Out of the box, EdgeDB provides excellent ORM-level bindings for Python and JavaScript. Both support coroutines and asynchronous I/O.

Schema Evolution

EdgeDB treats data schema changes as a version control issue. Changes automatically generate formal diffs.

The integrity of schema state is thoroughly validated, and revisions are signed with a cryptographic hash.

Fully Introspective

In EdgeDB, schema structure and metadata are accessible in queries, and are intelligently reflected in language bindings.

This enables rich metaprogramming capabilities, greatly assisting in building user interfaces.

Showcase

This section illustrates how EdgeDB and EdgeQL can be used in an implementation of an issue tracking system.

abstract concept Text: # This is an abstract object containing text. required link body -> str: constraint maxlength: # Maximum length of text is 10000 # characters. 10000 concept User is builtins.NamedObject # NamedObject is a standard abstract base class, # that provides a name link. abstract concept OwnedObject: # By default links are optional. required link owner -> User concept Status is builtins.Dictionary # Dictionary is a NamedObject variant, that enforces # name uniqueness across all instances if its subclass. concept Priority is builtins.Dictionary concept LogEntry is OwnedObject, Text: # LogEntry is an OwnedObject and a Text, so it # will have all of their links and attributes, # in particular, owner and text links. required link spent_time -> int atom issue_num_t is builtins.sequence # issue_num_t is defined as a concrete sequence type, # used to generate sequential issue numbers. concept Comment is Text, OwnedObject: required link issue -> Issue link parent -> Comment
concept Issue is builtins.NamedObject, OwnedObject, Text: required link number -> issue_num_t: readonly: true # The number values are automatically generated, # and are not supposed to be directly writable. required link status -> Status link priority -> Priority link watchers -> User: mapping: ** # The watchers link is mapped to User concept in # many-to-many relation. The default mapping is # *1 -- many-to-one. link time_estimate -> int link time_spent_log -> LogEntry: mapping: 1* # 1* -- one-to-many mapping. link start_date -> datetime: default := SELECT datetime::current_datetime() # The default value of start_date will be a # result of the EdgeQL expression above. link due_date -> datetime link related_to -> Issue: mapping: **
# EdgeQL query to extract all relevant issue details # traversing the hierarchy of Issue concept tree. SELECT Issue[ number, name, owner[ name # Extract the related User object and # include the value of the name link. ], watchers[ name ], priority[ name ], status[ name ], total_time_spent := ( SELECT agg::sum( Issue.time_spent_log.spent_time )   # The value of total_time_spent attribute       # is computed dynamically as a sum of all # spen_time values in all LogEntries # subordinate to this issue. ), start_date, due_date, related_to[ number, name, priority[ name ] ] ] WHERE Issue.number = $number # $number is a named query argument.
# An example JSON result of the query. [ { "id": "feae8382-5966-11e5-b3b1-b776f4595104", "number": 163, "name": "Boxes don't have round corners in IE6", "owner": { "id": "40dd6534-5967-11e5-9bda-3f5ef6a8f9be", "name": "Joe Doe" }, "watchers": [ { "id": "5b643fcc-5967-11e5-a4e1-5311fa226efc", "name": "Anna Smith" }, { "id": "6cb70818-5967-11e5-8a20-170ea3d3b6a3", "name": "Peter Mill" } ], "priority": { "id": "92bcd74a-5967-11e5-9ffa-2f6b459e5b3f", "name": "URGENT" }, "status": { "id": "a6a56de4-5967-11e5-8abd-1b738a8d5e0c", "name": "WON'T FIX" }, "total_time_spent": 0, "start_date": "2015-08-11T20:23:06+00:00", "due_date": "2015-08-11T22:00:06+00:00", "related_to": [ { "id": "2ff12728-5968-11e5-8cd1-8f6dd62ac490", "number": 121, "name": "Images have strange background in IE7", "priority": { "id": "92bcd74a-5967-11e5-9ffa-2f6b459e5b3f", "name": "URGENT" } } ] } ]
# Perform full-text search on all Text objects # in the database containing word "spam". SELECT Text[ type := __type__.name, body, # If a matched Text object is an Issue, # extract extra information. Issue.name, Issue.number, Issue.priority[ name ], Issue.status[ name ], # If a matched Text object is a LogEntry, # extract 'spent_time'. LogEntry.spent_time ] WHERE Text.body @@ "spam"
# An example JSON result of the query. [ { "id": "aba63e76-596d-11e5-b000-4b902bd4d4bc", "type": "LogEntry", "body": "Fixed spammy email title", "spent_time": 1.2 }, { "id": "0efd1f36-596d-11e5-b260-cfbc3fe107cf", "type": "Issue", "body": "Looks like spam to me.", "name": "Too many notifications sent", "number": 102, "priority": { "id": "796400b0-596d-11e5-b489-ff55f9e47278", "name": "LOW" }, "status": { "id": "9daec59a-596d-11e5-901b-3baab1c5ba88", "name": "NEW" } } ]
# Extract a tree of specific issue comments and # replies recursively. SELECT Comment[ owner[ name ], body, children := <parent* # Extract comments tree into 'children' # result attribute: # '*' signifies link recursion; # '<' requests backward traversal of # parent links so that the ordering # of objects is correct. ] WHERE Comment.issue.number = $number ORDER BY Comment.mtime
# An example JSON result of the query. [ { "id": "64d80c48-596a-11e5-bcef-23dcc79df376", "owner": { "id": "6cb70818-5967-11e5-8a20-170ea3d3b6a3" "name": "Peter Mill" }, "body": "Works now, thanks!", "children": [ { "id": "4aa31966-596b-11e5-ac48-a3cb996c225b", "owner": { "id": "5b643fcc-5967-11e5-a4e1-5311fa226efc", "name": "Anna Smith" }, "body": "In my IE6 too!", "children": [] } ] } ]
# An example of Python 3.5 code using the EdgeDB # Python binding. import edgedb from acmetracker import schema async def print_latest_activity(user, *, limit=10): # Extract and print last *limit* items # belonging to a *user*, excluding LogEntries. my_activity = schema.OwnedObject.select([ (schema.Issue, [ schema.Issue.number, schema.Issue.due_date, (schema.Issue.priority, [ schema.Priority.name ]) ]), edgedb.builtins.NamedObject.name, schema.Text.body ]).where( schema.OwnedObject.owner == user, edgedb.isnot(schema.OwnedObject, schema.LogEntry) ).orderby( (schema.OwnedObject.mtime, 'desc') ) # Iterate over results asynchronously async for entry in my_activity[:limit]: print(entry)
# Script output: <Issue "075561da-906e..." at 0x105817070> <Issue "0ed8a600-4ad6..." at 0x105817080> <Comment "1942a1fc-ffad..." at 0x105817090> <Comment "b2d0ec00-42fe..." at 0x1058170a0>

Why EdgeDB?

EdgeDB is a general-purpose database system that takes the best from relational databases and pushes it to the next level.

Why not use an ORM?

While a traditional ORM provides a better interface to an RDB than raw SQL, it still exposes the developer to low-level relational semantics, like different types of joins, foreign keys, many-to-many link tables.

Implicit queries and hard-to-control lazy loading have a significant impact on performance.

Why NoSQL isn’t a solution?

NoSQL document databases, while being simple to use initially, often lack ACID support, data consistency enforcement, and as such are unsuitable for a wide range of applications.

How EdgeDB is really different?

EdgeDB combines the simplicity of a NoSQL database with relational model’s powerful querying, strictness, consistency, and performance.

It is specifically designed to support and enhance application development workflow by providing flexible object storage model, expressive query language, robust and straightforward to use schema migration tools.

EdgeDB gets the most out of PostgreSQL features and extensibility, including native support for full-text search and geospatial data.