Postgres, GraphQL, DocumentDB. Pair with FerretDB for MongoDB compatibility.
206
One PostgreSQL database. Four query languages. Zero data duplication.
Query the same data with SQL, GraphQL, MongoDB, and DocumentDB APIs - all backed by a single PostgreSQL instance. Because sometimes you want ACID guarantees and schemaless flexibility.
This isn't a toy. It's a PostgreSQL 16 setup with:
mongosh, drivers, etc.)Same data, viewed through different lenses. Want to JOIN your documents with relational tables? Go ahead. Need GraphQL for your frontend but SQL for analytics? Done. Store unstructured logs in documents but enforce constraints on user data? Easy.
:17 - PostgreSQL 17 (latest, but FerretDB has compatibility warnings):16 - PostgreSQL 16 (recommended) - Rock solid with FerretDBdocker pull bedwards/pg-graph-doc:16
npm install
docker-compose up -d
That's it. You now have:
localhost:5432http://localhost:3000/rpc/graphqllocalhost:27017Let's create and query data using all four methods. Each uses different tables so you can run them all.
# Connect
PGPASSWORD=postgres psql -h 127.0.0.1 -U postgres -d postgres
# Or source env
source .env.example
psql "$POSTGRES_URL"
-- Create and query
CREATE TABLE employees (id SERIAL PRIMARY KEY, name TEXT, dept TEXT, salary INT);
INSERT INTO employees VALUES (1, 'Alice', 'Engineering', 120000), (2, 'Bob', 'Sales', 80000);
SELECT * FROM employees WHERE salary > 100000;
npm run sql "CREATE TABLE customers (id SERIAL PRIMARY KEY, email TEXT UNIQUE, tier TEXT)"
npm run sql "INSERT INTO customers (email, tier) VALUES ('[email protected]', 'premium')"
npm run sql "SELECT * FROM customers"
Remember: Tables need PRIMARY KEYs for GraphQL!
# GraphQL automatically sees your SQL tables
npm run gql "{ customersCollection { edges { node { id email tier } } } }"
# Mutations work too
npm run gql "mutation { insertIntocustomersCollection(objects: [{email: \"[email protected]\", tier: \"free\"}]) { records { id } } }"
# Relationships are automatic (if you have foreign keys)
npm run gql "{ customersCollection { edges { node { email ordersCollection { totalCount } } } } }"
# See what GraphQL generated
npm run sql "SELECT table_name FROM information_schema.tables WHERE table_schema = 'graphql'"
npm run sql "SELECT * FROM graphql.type"
npm run docdb inventory -i '{"sku":"WIDGET-001","name":"Blue Widget","stock":50,"specs":{"color":"blue","weight":"1.2kg"}}'
npm run docdb inventory -q '{"stock":{"$gt":10}}'
npm run docdb inventory -q '{}' -p '{"name":1,"stock":1}'
source .env.example
psql "$POSTGRES_URL" -c "SELECT collection_name, collection_id FROM documentdb_api_catalog.collections;"
psql "$POSTGRES_URL" -c "SELECT documentdb_core.bson_to_json_string(document) FROM documentdb_data.documents_5;" | head -20
# Install: brew install mongosh
source .env.example
mongosh "$MONGODB_URL"
// In mongosh
db.products.insertOne({name: "Laptop", price: 999, tags: ["electronics", "computers"]})
db.products.find({price: {$gt: 500}})
db.products.createIndex({name: 1}, {unique: true})
Or one-liners:
source .env.example
mongosh "$MONGODB_URL" --eval 'db.products.find().pretty()'
mongosh "$MONGODB_URL" --eval 'db.products.countDocuments()'
# Create collection with unique index and insert
npm run mongo -- users -c '{"email":1}' -o '{"unique":true}' -i '{"email":"[email protected]","name":"Alice"}'
# Insert more
npm run mongo -- users -i '{"email":"[email protected]","name":"Bob","age":30}'
# Query
npm run mongo -- users -q '{}' -p '{"name":1,"email":1}'
npm run mongo -- users -q '{"age":{"$exists":true}}'
# This will fail (unique constraint)
npm run mongo -- users -i '{"email":"[email protected]","name":"Alice2"}'
FerretDB stores MongoDB collections as PostgreSQL tables with BSON columns. Here's how to peek inside:
# List MongoDB collections
source .env.example
psql "$POSTGRES_URL" -c "SELECT collection_name, collection_id FROM documentdb_api_catalog.collections;"
# View documents (replace documents_N with actual table from catalog)
psql "$POSTGRES_URL" -c "SELECT documentdb_core.bson_to_json_string(document) FROM documentdb_data.documents_3;" | head -30
Expected output:
bson_to_json_string
----------------------------------------------------------------------------------------
{ "_id" : { "$oid" : "..." }, "email" : "[email protected]", "name" : "Alice", ... }
{ "_id" : { "$oid" : "..." }, "email" : "[email protected]", "name" : "Bob", ... }
Hot shit, right? MongoDB documents living as PostgreSQL rows, queryable with SQL.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Query Layer β
β psql run-sql.js run-gql.js run-docdb.js mongoshβ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β β β β
ββββββββββββΌβββββββββββββββΌβββββββββββββββ β
β β β
v v v
PostgreSQL PostgREST FerretDB
:5432 /rpc/graphql :3000 :27017
β β β
ββββββββββββββββ΄ββββββββββββββββββββββββββββ
β
v
ββββββββββββββββββββββββ
β PostgreSQL 16 β
β Extensions: β
β β’ pg_graphql β
β β’ documentdb β
β β’ pg_cron β
β β’ postgis β
ββββββββββββββββββββββββ
pg_graphql functions via HTTPAll configuration lives in .env.example:
POSTGRES_URL=postgres://postgres:postgres@localhost:5432/postgres
GRAPHQL_URL=http://localhost:3000/rpc/graphql
MONGODB_URL=mongodb://postgres:postgres@localhost:27017/postgres
Source it for CLI tools:
source .env.example
psql "$POSTGRES_URL"
mongosh "$MONGODB_URL"
Tables MUST have PRIMARY KEYs for pg_graphql:
# β
Works
npm run sql "CREATE TABLE items (id SERIAL PRIMARY KEY, name TEXT);"
npm run gql "{ itemsCollection { edges { node { name } } } }"
# β Doesn't work
npm run sql "CREATE TABLE broken (name TEXT);"
# GraphQL won't expose this table
postgres databasepostgres databasepostgres database (configurable in .env)They share the same PostgreSQL instance and can interoperate.
# Check services
docker-compose ps
# View logs
docker-compose logs -f pg-graph-doc
# Connect directly
docker exec -it pg-graph-doc psql -U postgres
# Restart everything
docker-compose down
docker-compose up -d
GraphQL returns empty: Missing PRIMARY KEY. Add one:
npm run sql "ALTER TABLE your_table ADD PRIMARY KEY (id);"
FerretDB connection refused: Wait 10s after startup for initialization.
DocumentDB functions missing: Extensions load at PostgreSQL startup. Check logs.
ISC
Content type
Image
Digest
sha256:4e217e086β¦
Size
500.6 MB
Last updated
11 months ago
docker pull bedwards/pg-graph-doc:16