Open-source project
supabase/pg_graphql avatar
supabase/pg_graphql

pg_graphql reflects a GraphQL schema from your tables, and its default build targets a different PostgreSQL than its dev database

GraphQL support for PostgreSQL

3,365 stars144 forksRustApache-2.0

At a glance

What is it?
supabase/pg_graphql is a Rust extension that turns an existing SQL schema into a GraphQL API inside the database process, with no extra server. Its default Cargo feature targets PostgreSQL 18 while the checked-in compose stack starts PostgreSQL 17, and the README documents no installation step at all.
Who is it for?
pg_graphql fits a team that already has a relational schema and wants a GraphQL API over it without standing up a separate service, since translation and query resolution both stay in the database and any language that can connect can query it. Two things to check before you rely on it.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 11 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • supabase/pg_graphql3,365 ★ · +12
Star history
Repository2026-09-242026-11-14
supabase/pg_graphql3.4k3.4k

12 Stars in the last 51 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

Tables become entrypoints, foreign keys become relationships

The mechanism is reflection. The SQL schema is what you write, and the GraphQL schema is derived from it:

sql
create table account(
    id serial primary key,
    email varchar(255) not null,
    created_at timestamp not null,
    updated_at timestamp not null
);

From that, each table receives an entrypoint in the top level Query type, described as a pageable collection, with relationships defined by its foreign keys. Tables also receive entrypoints for mutation that enable bulk operations for insert, update and delete. An enum declared in SQL, such as a blog_post_status type with PENDING and RELEASED values, becomes part of the same reflected schema. The wording is loose in one place, calling the mutation side a schema rather than a type, but the behaviour is the standard one: no schema is written by hand, and the foreign keys you already declared are what the GraphQL relationships are built from.

The default feature targets pg18, the compose database runs 17

The crate declares one Cargo feature per PostgreSQL major version, pg14 through pg18, each mapping to the matching pgrx and pgrx-tests feature. The default is a single entry: default = pg18. So a plain cargo build produces an extension for PostgreSQL 18 whether or not that is what you are running. The development environment in the repository disagrees with that default. docker-compose.yaml builds its database image from ./dockerfiles/db/Dockerfile and passes a build argument PG_VERSION of 17. Nothing in the compose file selects a Cargo feature, so the two version numbers in the repository, 18 in the manifest default and 17 in the compose build argument, are not connected by anything visible. Anyone reproducing the extension locally has to line those up by hand.

A cdylib that unwinds rather than aborts

The package builds as a cdylib, which is what a PostgreSQL extension written in Rust has to be, since the database loads it as a shared library rather than linking a binary. The file pg_graphql.control at the repository root is the extension's control file, the piece PostgreSQL reads to know what the extension is called and which script creates it. Two build settings are deliberate. Both the dev and the release profile set panic = unwind, so a Rust panic unwinds instead of aborting the whole database process, and the release profile adds opt-level 3, fat link time optimisation and codegen-units = 1, which trades build time for a smaller, faster library. The framework underneath is pinned hard: pgrx and pgrx-tests both sit at exactly 0.19.2, so the extension's PostgreSQL bindings and its test harness cannot drift apart.

The dev stack runs PostgREST and a GraphQL console beside it

The compose file describes three services, and only one of them is the extension. The database is container pg_db on host port 5406, with wal_level=logical and pg_stat_statements preloaded, a healthcheck running pg_isready as the postgres user every second, and a setup.sql mounted into docker-entrypoint-initdb.d so it runs on first start. The second service is postgrest/postgrest:v10.0.0 on port 3001, pointed at the same database with PGRST_DB_SCHEMA public and an anonymous role. The third is an nginx image serving GraphiQL on port 4000. So the reference environment puts a REST layer and a GraphQL console next to pg_graphql rather than replacing them. The credentials in that file are literals, POSTGRES_USER postgres with POSTGRES_PASSWORD password, which is fine for a local stack and would be a problem anywhere else.

The README contains no installation step

There is no build command, no CREATE EXTENSION statement and no package installation line anywhere in the readme. It links the documentation site at supabase.github.io/pg_graphql, links the source, points contributing at docs/contributing.md, and then explains the reflection model with one SQL example. The four checked claims in the middle of the file are Performant, Consistent, Serverless and Open Source, each with nothing attached: no measurement, no benchmark, no configuration, and for the consistency claim no statement of what is consistent with what. The rest of the documentation lives outside the readme in a docs directory with an mkdocs.yaml beside it, which is where installation, configuration and the reference material have to be.

The example calls a function whose extension it never creates

The worked example declares three tables and one enum. The account and blog tables are ordinary integer-keyed tables with timestamps, and blog has an owner_id with a foreign key to account. The third table, blog_post, is different in two ways: its primary key is a uuid with a default of uuid_generate_v4(), and the visible portion of that declaration stops partway through, after the blog_id column. The visible statements contain no CREATE EXTENSION line, so the function used to generate that key comes from an extension the example never installs. That is a small thing that will stop a reader at the first attempt, and it is invisible in the readme because the reader is meant to be reading the SQL as a picture of the schema rather than as a script to run.

Two dependencies are pinned exactly, the rest float

The manifest is precise where precision matters and loose where it does not. pgrx and pgrx-tests are pinned to the same exact version, 0.19.2, which is the correct treatment for a framework that has to match the PostgreSQL major version chosen at build time. Everything else moves on its own schedule: graphql-parser 0.4, serde and serde_json 1.0, itertools 0.10.3, rand 0.10, uuid 1, base64 0.13, lazy_static 1, bimap 0.6.3 with its serde feature, indexmap 2.2 and thiserror 2.0. One of them is configured more tightly than its version suggests: cached 0.46.0 is taken with default features off and only the proc_macro feature enabled, so a cache crate that would normally pull in a full async runtime contributes its macro support and nothing else. The package version, 1.6.2, matches the newest release tag exactly, and the crate is written against edition 2024.

Editorial conclusion

pg_graphql fits a team that already has a relational schema and wants a GraphQL API over it without standing up a separate service, since translation and query resolution both stay in the database and any language that can connect can query it. Two things to check before you rely on it. The default Cargo feature targets PostgreSQL 18, so building without choosing a feature gives you something the checked-in development database, which runs 17, was never tested against; pick your major version explicitly. And the README contains no installation step at all, so follow the documentation site rather than the readme for the actual commands. For a project that needs retries, persisted state or exactly-once behaviour, this is not that: it is a schema translation layer, and the compose stack shows what it expects to sit beside, a separate REST service and a GraphQL console.

Frequently asked questions

Can GraphQL be used with PostgreSQL through pg_graphql?

That is what the extension is for. It adds GraphQL support to a PostgreSQL database by reflecting a GraphQL schema from the existing SQL schema, and both the schema translation and the query resolution stay on the database server, so any programming language that can connect to PostgreSQL can query through GraphQL with no additional servers, processes or libraries.

What does pg_graphql generate from a table?

Each table receives an entrypoint in the top level Query type as a pageable collection, with relationships defined by its foreign keys, and entrypoints for insert, update and delete that support bulk operations. Types declared in SQL, such as an enum for blog post status, are part of the reflected schema as well.

Which PostgreSQL versions does pg_graphql support?

The crate carries a feature per major version, pg14 through pg18, and the default feature is pg18, so a build with no features selected targets PostgreSQL 18. The repository's compose file passes PG_VERSION 17 as a build argument to its database image, so the checked-in environment and the default build target are different versions.

How do I install the pg_graphql extension?

The README does not say. It links the documentation site at supabase.github.io/pg_graphql and the contributing guide in docs, and stops there. The repository itself is a Rust cdylib with a pg_graphql.control file, a bin directory and a sql directory, built against the pgrx framework pinned at version 0.19.2.

What dependencies does the pg_graphql crate have?

pgrx and pgrx-tests are pinned to exactly 0.19.2. The rest are graphql-parser 0.4, serde and serde_json 1.0, itertools 0.10.3, rand 0.10, uuid 1, base64 0.13, lazy_static 1, bimap 0.6.3 with serde, indexmap 2.2 and thiserror 2.0, plus cached 0.46.0 with its default features off and only the proc_macro feature enabled.

Official sources

  1. License: Apache-2.0
  2. Project website
  3. README
  4. Releases
  5. supabase/pg_graphql on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/supabase-pg-graphql.svg)](https://hysenlabs.com/projects/supabase-pg-graphql)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/supabase-pg-graphql.svg)](https://hysenlabs.com/projects/supabase-pg-graphql)

Was this useful?