Roadmap

Our mission is to empower developers with a database to build software faster and with less effort.

See what is already implemented as well as of what to expect in the future.

Design Principles

Ergonomics

The data model, EdgeQL, and all aspects of EdgeDB operation should be straightforward to learn and reason about, and the user experience should be a satisfying one.

Performance

EdgeQL features, language bindings, and tooling should be designed with high performance, low latency operation in mind.

Correctness

Correctness should never be sacrificed in favor of ergonomics or performance. Nonsensical operations must always generate an error.

Data Model

EdgeDB foundation: modern, type-safe, relational data model.

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

Type Systemdone

Object types, scalar types, arrays, tuples. Type composition and inheritance.

Functions and Operatorsdone

Support for polymorphic and generic functions and operators. Support for user-defined functions.

Constraintsdone

Support for arbitrary expressions in constraints. Multi-property constraints.

Introspectiondone

Complete schema introspection via EdgeQL and GraphQL queries.

Triggerspost 1.0

Ability to declare insert/update/delete triggers on object types.

abstract type Shape;

type Rectangle extending Shape {
  property width -> float64;
  property height -> float64;
}

type Circle extending Shape {
  property radius -> float64;
}


function area(shape: Rectangle)
  -> float64
from edgeql $$
  SELECT shape.width * shape.height
$$;

function area(shape: Circle)
  -> float64
from edgeql $$
  SELECT 3.1415 * shape.radius ^ 2
$$;
# A polymorphic EdgeQL query:
SELECT
  Shape {
    typename := .__type__.name,
    area := area(Shape)
  }
FILTER
  Shape IS (Circle | Rectangle);

EdgeQL

EdgeQL is the primary language of EdgeDB. It is used to define, mutate, and query data. Its main goals are to be readable, concise, and yet as powerful as SQL.

Object Hierarchiesdone

EdgeQL allows to fetch deep object hierarchies effortlessly, while applying filtering and sorting to the nested data.

Aggregate Functionsdone

Aggregate functions perform calculation on sets. EdgeDB supports many statistical and compositional aggregates.

Recursive Insertdone

Support for inserting a hierarchy of objects in a single atomic statement.

Type Safetydone

EdgeQL is a strongly typed functional language.

SELECT
  Movie {
    title,

    actors: {
      name,
      email
    } ORDER BY .name,

    avg_review := math::mean(
      .reviews.rating)
  }
FILTER
  datetime_get(.release_date,
              'year')
  IN {2018, 2019};


INSERT
Movie {
  title := 'Dune',
  actors := {
    (INSERT Person {
      name := 'Jason Momoa',
    }),
    (INSERT Person {
      name := 'Rebecca Ferguson',
    })
  }
};

Error Diagnosticspartly done

Errors should always be descriptive and besides just saying that something went wrong, they should suggest where exactly and how to fix that.

edgedb> 
....... 
....... 
SELECT Movie {
  avg_review := math::mean(.reviews.rating),
} FILTER .release_date > <datetime>'2018-01-01';

InvalidValueError: invalid input to mean(): not enough elements in input set
### SELECT Movie {
###   avg_review := math::mean(.reviews.rating),
###                 ^^^^^^^^^^ help: consider guarding with an IF expression
### } FILTER .release_date > <datetime>'2018-01-01';

Analytical Queriesplanned for 1.x

Generalized partitioning and window functions.

WITH
  Win AS WINDOW (WeeklyShowing
                GROUP BY .movie
                ORDER BY .week)

SELECT
  WeeklyShowing {
    movie,
    week,
    box_office,
    weekly_change :=
      (lag(.box_office) OVER Win) -
      .box_office
  }

FILTER
  .movie.name ILIKE 'Avengers%';
GROUP
  Movie {
    release_year := datetime_get(
      .release_date, 'year')
  }

BY
  Movie.genre, Movie.release_year

INTO
  MG

UNION
(
  genre := MG.genre,

  release_year := MG.release_year,

  avg_viewer_rating := math::mean(
    MG.reviews.rating),

  max_box_office := max(
    MG.box_office)
);

Basic IDE Supportdone

Official language highlighting packages for Atom, Sublime Text, Visual Studio Code, and Vim.

Language Server Protocolpost 1.0

Integrated LSP support allows code-completion and error highlighting for EdgeQL and EdgeDB SDL in IDEs.

Updatable Viewspost 1.0

Updatable views are an important mechanism for Database Views, GraphQL mutations and backwards-compatible schema migrations.

CREATE VIEW Comedy := (
  SELECT Movie {
    name,
  }
  FILTER
    .genre = 'Comedy'
);


INSERT
  Comedy {
    name := 'Long Shot',
    genre := 'Comedy'
  };

EdgePLpost 1.0

EdgePL is the imperative language used to write more complex functions and triggers.

CREATE FUNCTION fibonacci(n: int64) -> int64 FROM EdgePL $$

  let z := 0;
  let i := 0;
  let j := 1;

  if n < 1 {
    return 0;
  }

  while z <= n {
    z := z + 1;
    i, j := j, i + j;
  }

  return i;

$$;

GraphQL

EdgeDB ships with built-in GraphQL support.

Querying Object Typesdone

Any EdgeDB object type can be queried and introspected via GraphQL.

Querying Expression Aliasesdone

GraphQL can query EdgeQL Aliases in situations where a complex condition or a function call is needed.

Access Controlplanned for 1.x

Access control and business logic rules specified at the schema level and transparently enforced for GraphQL queries.

Mutationsdone

Support for mutation of object types and EdgeQL updatable views.

Subscriptionspost 1.0

Support for subscribing to a GraphQL endpoint to receive live updates.

fragment Groups on User {
  groups {
    name
  }
}

query {
  User(filter: {
    name: {ilike: "anna%"},
    age: {gt: 30}
  }) {
    name
    age

    settings(
      order: {name: {dir: ASC}},
      first: 5
    ) {
      name
      value
    }

    ...Groups
  }
}

Standard Library

The goal of the EdgeDB standard library is to include a large set of high-quality, consistent functions and operators.

Numericsdone

Common numeric functions, operators and literals. Strict handling of numeric precision.

Stringsdone

Common string functions and operators. Raw strings literals, regular expressions.

Date and Timedone

Strict, consistent handling of timezone-aware datetimes, local date, local time and durations.

JSONdone

Functions, operators and casts to traverse, extract and form JSON values.

GIS Extensionspost 1.0

Geometry and geography types and associated functions and operators.

Vectors and Matricespost 1.0

Support for efficient numeric vector and matrix computations.

db> 
SELECT (5 / 2, 5 // 2);
{2.5, 2}
db> 
SELECT 1.5n + 1.0;
QueryError: operator '+' cannot be
applied to operands of type
'std::decimal' and 'std::float64'
db> 
SELECT <local_date>'2019-01-01';
{'2019-01-01'}
db> 
SELECT <datetime>'2019-01-01';
InvalidValueError: missing required
timezone specification
db> 
SELECT <datetime>'2019-01-01 EST';
{'2019-01-01T05:00:00+00:00'}
db> 
... 
... 
... 
SELECT <json>Movie {
  title,
  year,
}
{'{"title": "Blade Runner", "year": 1982}',
'{"title": "Dune", "year": 2020}'}

SDL and Migrations

Core Supportdone

Support for declarative schema definition (SDL) and automatic DDL generation as migrations.

Rollback Supportplanned for 1.x

Support for migration rollbacks.

START MIGRATION TO {
  module default {
    # type Review { ... }
    # type Person { ... }

    type Movie {
      required property title -> str;
      required property year -> int64;

      multi link reviews -> Review;
      multi link directors -> Person;
      multi link cast -> Person;

      property avg_rating :=
        math::mean(.reviews.rating);
    }
  }
};

POPULATE MIGRATION;

COMMIT MIGRATION;

Tooling and Workflow Integrationin progress

edgedb migration command line utility for interactive migration generation and integration with version control systems.

$ 
ls -l dbschema/
default.esdl
$ 
edgedb create-migration
Migration 0001-initial created.
$ 
ls -l dbschema/migrations/
0001-initial.edgeql
$ 
edgedb migrate
Applying migrations:
  - 0001-initial      [OK]
$ 
sed -i 's/type Order/type Invoice/g' dbschema/default.esdl
$ 
edgedb create-migration
Detected schema changes:

  ALTER TYPE Order RENAME TO Invoice;

Confirm migration chunk [y,n,a,d,e,?] y
Migration 0002-order-rename created.
$ 
edgedb migrate
Applying migrations:
  - 0002-order-rename [OK]

Multiple Schema Versions and Live Migrationspost 1.0

Staggered update deployment and continuous integration require both the old and the newschema to be available at the same time. On large deployments there should be a way to migrate without blocking production traffic.

prod-02 $ 
env EDGEDB_SCHEMA_TAG=v99 run_server.py

### Meanwhile, on a devbox: ###
devbox $ 
edgedb migration create --tagged
Migration 0100-schema-updates created.
devbox $ 
deploy_to_prod.sh --to prod-01
prod-01) Applying migrations:
prod-01)   - 0100-schema-updates [OK]
prod-01) Restarting server:
prod-01)   env EDGEDB_SCHEMA_TAG=v100 run_server.py

Access Control

Fine-grained Data Access Control Rulesplanned for 1.x

Data access control is one of the most ubiquitous types of business logic in applications. Supporting flexible access rules at the schema level benefits performance, security, and development productivity through the separation of concerns.

type Service {
  property name -> str
};

type ServiceProvider extending User;

type ServiceManager extending User {
  link service -> Service;
};

type Order {
  link service -> Service;
  link provider -> ServiceProvider;
  property total -> decimal;

  policy sp_access on SELECT {
  WHEN
    (GLOBAL user) IS ServiceProvider
  CHECK
    .provider = (GLOBAL user)
  };

  policy sm_access on SELECT {
  WHEN
    (GLOBAL user) IS ServiceManager
  CHECK
    .service = (GLOBAL user).service
  }
}
db> 
... 
... 
... 
SET GLOBAL user := (
 SELECT ServiceProvider
 FILTER .name = 'Roofing inc.'
);
SET GLOBAL
db> 
SELECT count(Order);
{10}
db> 
... 
... 
... 
SET GLOBAL user := (
 SELECT ServiceManager
 FILTER .name = 'Roof Manager'
);
SET GLOBAL
db> 
SELECT count(Order);
{123}

Database Viewspost 1.0

Schema introspection has powerful applications in automatic or assisted generation of APIs, user interfaces and application bindings. However, there are security and API surface exposure concerns when it comes to exposing the database schema. Database views can help with that.

Combined with fine-grained data access control rules, this can significantly reduce the need to write backend code.

database view GraphQLView {
  # Export all types from the
  # "users" module:
  type users::*;

  # Export the "Order" type as
  # "Invoice" with a limited
  # set of properties:
  type orders::Order as Invoice {
    link user;

    property subtotal;
    property taxes;

    property total :=
      .subtotal + .taxes;
  }
};

# Start a GraphQL endpoint
# exposing the "GraphQLView"
# view:
CONFIGURE SYSTEM INSERT Port {
  protocol := "graphql+http",
  database := "GraphQLView",
  port := 80,
  user := "graphql",
};

Caching

Stored Computablespost 1.0

Stored computable properties and links with flexible invalidation policy:

  • valid forever (computed exactly once);
  • valid for a period of time;
  • invalidated automatically when source data changes.
type Movie {
  link reviews -> Review;
  property avg_rating {
    expr := math::mean(.reviews.rating);
    stored := true;
    valid_for := <duration>'1 hour'
  }
}

Client Language Bindings

EdgeDB will provide native idiomatic drivers for all popular platforms. Drivers for Go, Rust, Ruby, Java, and .NET are on our list.

In any EdgeDB client library data can be requested either as high-level objects or JSON.

Pythondone

A fast Python driver supporting both blocking IO and async/await paradigms.

JavaScript / TypeScriptdone

An idiomatic JavaScript driver with support for both async/await and callback APIs.

HTTPdone

EdgeDB can expose both EdgeQL and GraphQL via an HTTP endpoint.

# Use "query()" to fetch data
# as rich Python objects.
#
# (If JSON is needed, just use
# the "query_json()" method - no
# need to change the query).
movies = await conn.fetchall('''
  SELECT
    Movie {
      title,

      actors: {
        name,
        email
      } ORDER BY .name,

      avg_review := math::mean(
        .reviews.rating)
    }
  FILTER
    datetime_get(.release_date,
                'year')
    IN {2018, 2019};
''')

print(movies)

Query Builders and Schema Reflectionplanned for 1.x

EdgeDB bindings for all languages will have a query building API.

from edgedb import qb, connect
from my.app import esdl as schema


get_movies_query = qb.shape(schema.Movie, [
  'title',

  qb.shape(schema.Movie.actors, [
    'name',
    'email'
  ]).order_by('.name'),

  qb.computable('avg_review', qb.math.mean(
    schema.Movie.reviews.rating))
]).filter(
  qb.IN(
    qb.datetime_get(schema.Movie.release_date, 'year'),
    {2018, 2018}
  )
)


conn = connect()
data = conn.query_json(get_movies_query)
print(data)