EdgeDB has recently reached the awesome milestone of 1.0! This caught my eye for a number of reasons, not least because the team responsible, Magic Stack, also wrote uvloop
and asyncpg
, both highly respected Python libraries. I’ve been playing with it over the past few days and wanted to tell you why EdgeDB is worth watching.
Before I go into my list though, I thought it would be worth giving you the EdgeDB elevator pitch.
What is EdgeDB?
EdgeDB is an open source “graph-relational” database, that aims to address the poor developer UX when working with incumbent relational systems such as Postgres or MariaDB. It bills itself as a reimagining of the entire database stack focused on ergonimics and integrations with modern developer tooling. Not only that, it shouldn’t cause operational headaches as it’s powered by PostgreSQL, so existing toolchains and monitoring stacks should work out-of-the-box.
Okay, so now you know what it is, what’s so great about it?
#1 - It helps reduce cognitive load through elegant typing
Cognitive load relates to the amount of information that you can hold in your working memory at any one time. Overloading your brain causes fatigue, irritability and leads to worse outcomes for the code you’re working on. Remaining in a state of flow for as long as possible is what we aim for as engineers, and context switching is a flow killer.
This is why developer ergonmics are so important. Databases like MongoDB certainly appeal to my sensibilities as someone who works mostly with a TypeScript stack - Mongo’s query language is based on JSON, which is easy to grok when you’re wrangling those objects elsewhere in your application.
Let’s look at an example. Here’s a query taken from the launch announcement that grabs movies which feature a certain actor:
select
Movie {
title,
rating := math::mean(.ratings.score)
actors: {
name
} order by @credits_order
limit 5,
}
filter
"Zendaya" in .actors.name
And here’s the same query as expressed within a TypeScript application:
e.select(e.Movie, movie => ({
title: true,
rating: e.math.mean(movie.reviews.score),
actors: actor => ({
name: true,
order_by: actor["@credits_order"],
limit: 5,
}),
filter: e.op("Zendaya", "in", movie.actors.name),
}))
Not only is is essentially identical to parse, it supports typing so your Intellisense will be able to pick this up as you go. It’s so refreshing to see a database designed with these ergonomics as a first class citizen.
On top of the TypeScript bindings, there are also packages for Python, Deno and Go.
#2 - You (probably) know how to run it already
Because EdgeDB is built atop PostgreSQL, existing tooling, monitoring and orchestration tools will continue to work, removing the headache of tool sprawl for the team operating this database.
For me, deploying was as easy as using the official Docker image:
docker run --name edgedb -d \
-e EDGEDB_SERVER_SECURITY=insecure_dev_mode \
edgedb/edgedb
Or a Docker Compose file:
version: "3"
services:
edgedb:
image: edgedb/edgedb
environment:
EDGEDB_SERVER_SECURITY: insecure_dev_mode
volumes:
- "./dbschema:/dbschema"
- "./credentials:/root/.config/edgedb/credentials"
ports:
- "5656"
It’s important not to underestimate how big a deal this is - your existing deployment systems - fly.io, AWS ECS, all of it will work with EdgeDB. All of the ergonomics, none of the operational headaches.
#3 - It’s less chatty than traditional systems
GraphQL opened my eyes to just asking the server for what you need, instead of having to drag back whatever the server was programmed to return. This is especially important when building for mobile users, some of whom are still rocking 3G networks, bringing modern web applications to a screetching halt.
EdgeDB allows you to combine almost limitless fetch or mutate queries into one query. This is awesome, because it increases the overall availability of a given node to respond to other queries, rather than inefficiently locking here and there.
Great news for your users, better news for your ops team!
#4 - It’s not SQL
Okay… don’t @ me!
Some people love SQL - but not me. I find the sheer load on my mind trying to parse large queries is just too much to handle when working on complex systems. Not only that but the error handling in many RDMS systems is head-bangingly awful. I know I harp on about ergonomics but it really does make a difference when you’re working to a deadline.
Let’s take that example from the typing section:
select
Movie {
title,
rating := math::mean(.ratings.score)
actors: {
name
} order by @credits_order
limit 5,
}
filter
"Zendaya" in .actors.name
The (almost) equivalent SQL is:
SELECT
title,
Actors.name AS actor_name,
(SELECT avg(score)
FROM Movie_Reviews
WHERE movie_id = Movie.id) AS rating
FROM
Movie
CROSS JOIN LATERAL (
SELECT name
FROM
Movie_Actors
INNER JOIN Person
ON Movie_Actors.person_id = Person.id
WHERE Movie_Actors.movie_id = Movie.id
ORDER BY Movie_Actors.credits_order
FETCH FIRST 5 ROWS ONLY
) AS Actors
WHERE
'Zendaya' IN (
SELECT Person.name
FROM
Movie_Actors
INNER JOIN Person
ON Movie_Actors.person_id = Person.id
)
… I think I know which one I prefer.
#5 - GraphQL is available out of the box
One potential roadblock for EdgeDB adoption could be the introduction of another query language into an existing application. GraphQL is a popular implementation of a native graph query language on top of multiple resolvers, which could be a SQL database, a REST API or basically anything underneath.
EdgeDB supports GraphQL out of the box, which means connecting it into an existing GraphQL-enabled application should be a snap.
Graphs are eating the world. Quickly traversing complex relationships is quickly becoming a basic requirement for web applications so it’s great to see a native DB embracing this way of working.