SQL was designed in the 1970s. It works, but writing a 3-level nested join with aggregations feels like wrestling an octopus. EdgeDB speaks modern syntax that reads like English.
What EdgeDB Offers
EdgeDB Cloud free tier:
- 1 GB storage on managed cloud
- EdgeQL — modern query language (no JOINs needed)
- TypeScript query builder — type-safe, no strings
- Built-in auth — email, OAuth, WebAuthn
- HTTP API — query via REST or GraphQL
- Migrations — auto-generated from schema changes
- Open source — self-host unlimited
Quick Start
curl --proto '=https' -sSf1 https://sh.edgedb.com | sh
edgedb project init
Schema (Readable)
# dbschema/default.esdl
module default {
type User {
required name: str;
required email: str { constraint exclusive; };
multi posts := .<author[is Post];
created_at: datetime { default := datetime_current(); };
}
type Post {
required title: str;
content: str;
published: bool { default := false; };
required author: User;
multi tags: Tag;
created_at: datetime { default := datetime_current(); };
}
type Tag {
required name: str { constraint exclusive; };
}
}
edgedb migration create
edgedb migrate
EdgeQL (No JOINs!)
# Get posts with author — no JOIN syntax
SELECT Post {
title,
content,
published,
author: { name, email },
tags: { name }
}
FILTER .published = true
ORDER BY .created_at DESC
LIMIT 10;
# Insert with nested creation
INSERT Post {
title := 'EdgeDB is Amazing',
content := 'Here is why...',
author := (SELECT User FILTER .email = 'alice@example.com'),
tags := {
(INSERT Tag { name := 'database' } UNLESS CONFLICT ON .name ELSE Tag),
(INSERT Tag { name := 'edgedb' } UNLESS CONFLICT ON .name ELSE Tag)
}
};
# Aggregation
SELECT User {
name,
post_count := count(.posts),
published_count := count(.posts FILTER .published = true)
}
ORDER BY .post_count DESC;
TypeScript Query Builder (Fully Typed)
import { createClient } from 'edgedb';
import e from './dbschema/edgeql-js'; // Auto-generated
const client = createClient();
// Fully typed query — autocomplete for every field
const posts = await e.select(e.Post, (post) => ({
title: true,
content: true,
author: { name: true, email: true },
tags: { name: true },
filter: e.op(post.published, '=', true),
order_by: { expression: post.created_at, direction: e.DESC },
limit: 10
})).run(client);
// Type of posts is automatically inferred!
// { title: string; content: string | null; author: { name: string; email: string }; ... }[]
HTTP API
# Query via HTTP
curl -X POST 'http://localhost:10700/branch/main/edgeql' \
-H 'Content-Type: application/json' \
-d '{"query": "SELECT Post { title, author: { name } } LIMIT 5"}'
Why EdgeDB
| EdgeDB | PostgreSQL + ORM |
|---|---|
| No JOINs needed | JOIN syntax |
| Schema = readable | DDL + ORM models |
| Auto-generated types | Manual types |
| Built-in auth | Add auth library |
| Auto migrations | Write migrations |
Need structured data from websites? Check out my web scraping actors on Apify.
Need database architecture help? Email me at spinov001@gmail.com.
Top comments (0)