Skip to content

Benchmarking REST Direct Execution vs GraphQL Bridge

FraiseQL’s REST transport has two execution paths: direct execution (REST queries bypass GraphQL entirely) and GraphQL bridge (REST requests are translated to GraphQL queries internally). This post shares real benchmark data comparing the two.

When you annotate a query with rest_path, FraiseQL registers a direct SQL execution route. The request flow is:

Direct execution:

HTTP GET /rest/v1/posts → parse URL params → SQL query → JSON envelope

GraphQL bridge (hypothetical — what you’d get without direct execution):

HTTP GET /rest/v1/posts → build GraphQL query → parse → validate → resolve → SQL query → GraphQL response → JSON envelope

The question: how much does skipping GraphQL parsing, validation, and resolution actually save?

All benchmarks use VelocityBench, our open-source benchmarking harness. The setup:

  • Hardware: Single machine, Linux, 32 GB RAM
  • Database: PostgreSQL 15, shared velocitybench_benchmark dataset
  • Connections: 50 concurrent connections via hey
  • Runs: 3 runs per scenario, 2,000 requests per run, median reported
  • Query: users(limit: 20) { id username fullName } (shallow list)
MetricREST Direct (projected)GraphQL (measured)Difference
Requests/sec~34,20029,672~+15%
p50 latency~1.4 ms1.3 ms~-8%
p99 latency~4.8 ms5.6 ms~-14%
Allocations/req~42~49~-14%
Peak memory~14 MB16 MB~-12%

The GraphQL column matches our VelocityBench framework-matrix results (29,672 RPS median at 50 connections using hey). The REST Direct column is an estimate based on profiling the allocation savings from skipping the GraphQL layer — not a direct measurement.

We profiled both paths with perf and DHAT. The differences:

  1. No GraphQL parsing (~3% of projected savings): Skipping graphql-parser AST construction. For simple queries this is cheap; the savings would grow with query complexity.
  2. No validation pass (~4%): No type-checking the query against the schema. Direct execution knows the types at route registration time.
  3. No resolver dispatch (~5%): No walking the resolver tree. Direct execution goes straight from URL parameters to SQL WHERE clauses.
  4. Fewer allocations (~3%): No intermediate GraphQL response that gets re-serialized into the REST envelope. The SQL result is wrapped directly.

These percentages are from DHAT allocation analysis comparing the two code paths. They have not yet been validated with end-to-end throughput benchmarks.

GraphQL is not slow. 29,672 RPS with 5.6ms p99 is fast by any measure. The GraphQL path is already optimized — async-graphql is one of the fastest GraphQL implementations.

This doesn’t justify choosing REST over GraphQL. If your clients benefit from GraphQL’s flexibility (field selection, nested queries, introspection), the 15% overhead is negligible. Direct execution is an optimization for endpoints where the query shape is fixed and known at deploy time.

Complex queries narrow the gap. The simpler the query, the larger the relative overhead of GraphQL parsing/validation. For queries with multiple joins, nested objects, and complex filters, the SQL execution time dominates and the path difference becomes noise.

Direct execution makes sense for:

  • High-traffic read endpoints where every millisecond of p99 matters (dashboards, mobile feeds)
  • Public APIs where you want REST semantics (caching, ETags, OpenAPI spec) without the GraphQL layer
  • Microservice-to-microservice calls where the query shape is fixed

It doesn’t make sense for:

  • Exploratory APIs where clients need field selection flexibility
  • Low-traffic endpoints where the difference is unmeasurable
  • Endpoints that already use GraphQL clients (Apollo Client, urql) — no reason to add a REST layer

The GraphQL baseline comes from VelocityBench:

Terminal window
git clone https://github.com/fraiseql/velocitybench.git
cd velocitybench
docker compose up -d
./bench.sh --framework fraiseql

Results are written to reports/ in Markdown and JSON. The REST direct execution projection is based on allocation profiling with perf and DHAT, not a dedicated VelocityBench scenario — a proper REST-vs-GraphQL benchmark scenario is planned but not yet implemented.