
PostgreSQL 19's graph story changed during beta testing. The project removed native SQL/PGQ support in PostgreSQL 19 Beta 4, released on September 24, 2026. Earlier previews of CREATE PROPERTY GRAPH and GRAPH_TABLE therefore describe functionality that is no longer included in the release. The project released PostgreSQL 19 RC 1 on October 15, 2026; SQL/PGQ remains outside the release.
Graph-shaped data still fits PostgreSQL. The practical questions are how to represent relationships, which queries ordinary SQL handles well, and when an extension or separate graph engine helps. This guide covers those choices with a service-dependency example.
PostgreSQL 19 graph describes working with connected data in PostgreSQL 19, rather than a separate database product. Entities such as services, accounts, and devices become rows. Relationships become foreign keys or rows in an edge table. A graph query follows those relationships to answer questions about dependencies, reachability, or shared connections.
One application of this approach is building knowledge graphs for enterprise AI agents. A knowledge graph represents business entities and the relationships between them, giving agents structured context they can retrieve to ground their responses in an organization’s own data. For example, an agent investigating a service outage could follow dependency and ownership relationships to identify affected services and responsible teams. When those records already live in PostgreSQL, relational tables and graph queries can provide a foundation for that context.
The distinction from the withdrawn feature matters. SQL/PGQ was intended to provide graph-pattern syntax over relational data, including a graph definition and a GRAPH_TABLE construct. The original PostgreSQL implementation announcement described rewriting these queries into ordinary relational queries. That historical design should not be confused with the capabilities retained in PostgreSQL 19.
For the current release, start with SQL joins and recursive common table expressions, or CTEs. They can express graph traversals without requiring a graph extension.
Consider a service catalog. Each service has an identifier and a name. A dependency records that one service calls another. The edge direction has a precise meaning: checkout-api → payments-api means checkout depends on payments. To investigate a payments outage, follow incoming edges toward its dependents.
A fixed-depth traversal can chain joins between dependency rows. A recursive CTE starts with a seed result and repeatedly evaluates a recursive term against the previous iteration's working rows. This supports variable-depth traversal, including graphs whose depth is not known when the query is written.
The query must define what counts as an answer. Finding every path, listing each reachable service once, and calculating a shortest weighted route are different tasks. A traversal that is correct for one can waste work or produce misleading output for another.

The useful graph building blocks are established PostgreSQL capabilities, not a new native graph subsystem.
Relational integrity. Primary keys identify vertices, while foreign keys keep edge endpoints attached to valid rows. A composite primary key can prevent duplicate relationships when the model allows only one edge per ordered pair. PostgreSQL's constraint documentation defines these guarantees.
Recursive traversal. WITH RECURSIVE supports repeated expansion. PostgreSQL also provides SEARCH and CYCLE clauses for traversal ordering information and cycle tracking. These clauses help express traversal logic; they do not turn a query into a specialized shortest-path algorithm.
SQL composition. Traversal results can feed joins and aggregates, letting a dependency analysis incorporate service owners, costs, or incident records. This is useful when the answer combines connectivity with ordinary reporting.
Extensions for particular workloads. Apache AGE adds property-graph querying with openCypher. Its download page lists releases by PostgreSQL version, but does not list PostgreSQL 19 at the time of writing. Treat extension compatibility as a separate requirement, not a consequence of upgrading PostgreSQL. For weighted routing, pgRouting is another specialized option whose supported database versions must be checked independently.
Create the following small dataset in a scratch database. It deliberately includes a cycle so that termination is part of the example:
CREATE TABLE service (
id integer PRIMARY KEY,
name text NOT NULL UNIQUE
);
CREATE TABLE depends_on (
source_id integer REFERENCES service(id),
target_id integer REFERENCES service(id),
PRIMARY KEY (source_id, target_id)
);
CREATE INDEX depends_on_incoming
ON depends_on (target_id, source_id);
INSERT INTO service VALUES
(1, 'checkout-api'), (2, 'payments-api'),
(3, 'identity-service'), (4, 'billing-worker');
INSERT INTO depends_on VALUES
(1, 2), (2, 3), (4, 2), (3, 1);The primary key indexes outgoing lookups. The second index puts target_id first for incoming lookups. This follows PostgreSQL's multicolumn B-tree indexing behavior; index selection still depends on the actual plan and data distribution.
To find services that depend on identity-service, directly or indirectly, traverse edges backward:
WITH RECURSIVE impact(id, depth, path) AS (
SELECT id, 0, ARRAY[id]
FROM service
WHERE name = 'identity-service'
UNION ALL
SELECT d.source_id, i.depth + 1,
i.path || d.source_id
FROM impact AS i
JOIN depends_on AS d ON d.target_id = i.id
WHERE i.depth < 8
AND NOT (d.source_id = ANY(i.path))
)
SELECT s.name, min(i.depth) AS hops
FROM impact AS i
JOIN service AS s ON s.id = i.id
WHERE i.depth > 0
GROUP BY s.id, s.name
ORDER BY hops, s.name;The array path records visited vertices. The guard prevents revisiting a vertex within the same path, while the depth limit bounds exploration to eight hops. The outer aggregation returns each dependent once with its minimum hop count among the explored paths. It is not a weighted shortest-path calculation.
The result is payments-api at one hop, followed by billing-worker and checkout-api at two. This identifies structural dependencies, not proof that every dependent is currently failing.
Incident impact. The recursive query above traces upstream dependents. A production model could attach ownership and criticality to service rows, then join those attributes into the incident response result.
Shared dependencies. Two services that call the same backend may share a failure point. Using the same dataset:
SELECT a.name AS service_a, b.name AS service_b,
backend.name AS shared_dependency
FROM depends_on AS x
JOIN depends_on AS y
ON x.target_id = y.target_id
AND x.source_id < y.source_id
JOIN service AS a ON a.id = x.source_id
JOIN service AS b ON b.id = y.source_id
JOIN service AS backend ON backend.id = x.target_id;This returns checkout and billing as consumers of payments. The inequality excludes self-pairs and mirrored duplicates.
Account investigations. The same modeling approach can connect accounts to shared devices or payment instruments. Shared infrastructure is an investigation lead, not evidence of fraud by itself. Direction, timestamps, and relationship meaning determine whether the connection is useful.
For a transactional (OLTP) application, one- or two-hop relationship lookups can stay in PostgreSQL. A high volume of application writes is also a reason to keep PostgreSQL as the transactional store. The case for separate graph compute starts with analytical (OLAP) workloads: traversing beyond three hops through many branching dependencies, matching complex patterns, or aggregating impact across a large service estate.
PuppyGraph is the first and only zero-ETL graph query engine for analytical queries over existing data. It maps existing tables into a graph schema and queries them through openCypher and Gremlin, without requiring a separate graph database copy. You can reuse the graph schema design behind the service catalog: the same service identifiers, dependency directions, and properties become nodes and edges through PuppyGraph's own mappings. PuppyGraph compiles a graph query into a plan of node and edge operators that runs in its own distributed engine. Because the query is represented as graph operators end to end, the engine optimizes specifically for multi-hop traversals. SQL sources receive only simple projection / filter SQL, while PuppyGraph performs the traversal and aggregation. PostgreSQL continues handling application writes; direct reads still consume source resources.
Federated graph queries provide a second reason to add PuppyGraph, even when individual paths are short. Service ownership might live in PostgreSQL, deployment history in Snowflake, and telemetry in Databricks Delta Lake tables. With shared service identifiers mapped into one graph schema across those sources, an analyst could follow a dependency chain from PostgreSQL and correlate affected services with deployments or telemetry in the other sources in one graph query. The existing model extends across those stores, without a graph-specific ETL pipeline to consolidate their data. This adds cross-source investigation to the operational catalog while keeping each dataset in its existing store.



Query ergonomics. With SQL/PGQ removed, graph patterns must be expressed through SQL or a separate implementation. Recursive SQL can answer reachability questions correctly, but applications must maintain traversal direction, cycle handling, and result semantics.
Intermediate-result growth. The example enumerates paths before grouping results. Multiple paths can reach the same vertex, so final deduplication does not eliminate the work of exploring them. A depth bound also limits completeness: dependencies beyond the chosen horizon are absent.
Performance under application load. Graph exploration competes with other queries for database resources. Use EXPLAIN ANALYZE and buffer statistics on representative data to inspect actual row counts, access paths, and I/O. EXPLAIN ANALYZE executes the query, so test expensive traversals in a suitable environment. Measure high-degree vertices and concurrent traffic, not only this tiny example.
SQL/PGQ's removal does not make PostgreSQL unsuitable for relationships. It changes the available interface; workload shape still determines whether relational execution meets the requirement.
PostgreSQL is a sensible starting point when relationships belong to an existing transactional application and queries have predictable scope. A separate graph system deserves evaluation when graph exploration becomes a major workload or the required data spans several stores.
For example, Neo4j documents importing relational data into its graph store. When PostgreSQL remains the source of record, keeping that imported data current becomes part of the design. The following table frames the evaluation.
Dedicated graph databases vary substantially; a graph label alone does not establish a performance advantage. Benchmark equivalent answers on equivalent data. PuppyGraph offers another deployment shape: a separate graph compute layer over existing stores.
Teams deploy, secure, and upgrade the graph compute layer. Reads from PostgreSQL use the configured database credentials, while PuppyGraph roles govern which graph operations each user can perform. Verify access through both layers. Direct reads and local tables also have different freshness behavior: direct reads query source tables at query time, while optional local tables serve cached rows. Include source read load and cache-loading cadence in the evaluation.
PostgreSQL 19 can handle graph-shaped data through relational modeling and recursive SQL. Whether it fits your use case depends on traversal complexity, query performance, and the load on your existing database. Keep application writes and relationship queries that perform well in PostgreSQL. Evaluate a separate graph compute layer when deep traversals or aggregations over large connected subgraphs exceed your latency budget, or when an investigation requires following relationships across PostgreSQL and warehouse or lakehouse data.
Try the forever-free PuppyGraph Developer Edition and book a demo with the team to see how openCypher and Gremlin queries connect PostgreSQL data with warehouse and lakehouse tables, with no graph-specific ETL, for dependency analysis across your existing stores.
Get started with PuppyGraph!
Developer Edition
Enterprise Edition