Our mission is to empower developers with a better database.

This page is an overview of what is already implemented as well as of what to expect in the future.

Download Alpha 1

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.0

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 ViewsPlanned for 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 ViewsDone

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

Access ControlPlanned for 1.0

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

MutationsPlanned for 1.0

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.

> SELECT (5 / 2, 5 // 2); {2.5, 2} > SELECT 1.5n + 1.0; QueryError: operator '+' cannot be applied to operands of type 'std::decimal' and 'std::float64' > SELECT <local_date>'2019-01-01'; {'2019-01-01'} > SELECT <datetime>'2019-01-01'; InvalidValueError: missing required timezone specification > SELECT <datetime>'2019-01-01 EST'; {'2019-01-01T05:00:00+00:00'} > 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.0

Support for migration rollbacks.

CREATE MIGRATION initial TO {
  # 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);
  };

COMMIT MIGRATION initial;

Tooling and Workflow IntegrationPlanned for 1.0

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

$ ls -l esdl/migrations/ default.esdl $ edgedb migration create Migration 0001-initial created. $ ls -l esdl/migrations/ 0001-initial.edgeql $ edgedb migration commit Applying migrations: - 0001-initial [OK] $ sed -i 's/type Order/type Invoice/g' esdl/default.esdl $ edgedb migration create Detected schema changes: ALTER TYPE Order RENAME TO Invoice; Confirm migration chunk [y,n,a,d,e,?] y Migration 0002-order-rename created. $ edgedb migration commit 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.0

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

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",
};

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.

Caching

Stored ComputablesPost 1.0

type Movie {
  link reviews -> Review;
  property avg_rating {
    expr := math::mean(.reviews.rating);
    stored := true;
    valid_for := <duration>'1 hour'
  }
}

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.

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 / TypeScriptIn Progress

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

HTTPDone

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

# Use "fetchall()" to fetch data
# as rich Python objects.
#
# (If JSON is needed, use the
# "fetchall_json()" method with
# the same 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.0

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.fetchall_json(get_movies_query)
print(data)