PRODSovereign European BaaS platformOpen Dashboard →

Performance · 10 min read

PostgREST vs Hasura vs a custom API

Affane Daylami · Fondateur · May 15, 2026

Back to blog

Three architectures answer the same question, each in its own way: how to plug an API into a Postgres database without writing everything by hand. PostgREST generates a REST API from your SQL schema. Hasura generates a GraphQL API with its own permissions system and extension points for your business logic. A custom API, in Node.js or elsewhere, gives you total control, at the cost of coding everything yourself. The right choice depends less on raw performance and more on where you want your business logic to live.

This English text was generated automatically from the French original and has not been reviewed yet.

This article extends two comparisons already published on this blog: our review of PostgREST compatibility and its alternatives and our comparison dedicated to GraphQL layers on Postgres. Here the angle changes: a decision grid between three ways to build an API layer, with Hasura treated for its permissions and business extension points rather than its GraphQL syntax, and a hand-written API as an option in its own right, not a simple "else" line at the bottom of the table.

The essentials

  • PostgREST auto-generates a REST API from the Postgres schema — no arbitrary business logic possible, the RLS remains the only security boundary.
  • Hasura adds its own permissions system by role and table, Actions to connect a business webhook, Event Triggers, and RESTified endpoints on top of its GraphQL engine.
  • A custom API (Node.js, Express, Fastify...) gives total control over business logic, validation and auth — at the cost of writing, testing and maintaining everything yourself.
  • On Aurabase, the PostgREST layer is a real instance; business logic beyond CRUD goes through SQL functions exposed in RPC or through Edge Functions, not through a separate Node server to host.
  • The three approaches are not necessarily mutually exclusive: combining PostgREST for CRUD and a custom API for sensitive operations is a common pattern in production.
#
Overview

The real choice: who writes the business logic, and where

The question “PostgREST or Hasura or custom API” hides a more useful question: who writes your business logic, with which tool, and who uses this code in production? The three architectures respond differently, and this difference structures everything else — security, speed of implementation, technical debt in the long term.

Origin of the APIGenerated from SQL schemaGenerated from the schema, via the GraphQL Hasura engineWritten route by route, by hand
Custom business logicSQL functions (RPC) onlyActions (webhook) + Event TriggersAny code, without tool constraints
Security ModelRLS Postgres, role driven by JWTPermissions specific to role/table, not a delegation to the RLSWhat you code (middleware, ORM, optional RLS)
To accommodate in additionNothing — a lightweight binaryThe Hasura engine, with its own metadata baseThe complete application server
Learning curveLow if the team already knows how to write SQLMedium — a new permissions and config systemNothing on the tool, but everything else to design
POSTGRESTHASURACUSTOM API

None of the three columns is strictly better: each shifts the work elsewhere. PostgREST moves it to SQL, Hasura to configuration and webhooks, a custom API to classic application code.

#
Reminder

PostgREST: the API as a direct reflection of the schema

PostgREST transforms your Postgres schema into a REST API — filters, relationship embedding, RPC, RLS driven by JWT — with no backend to write. We detail this scope in depth in our article on its real compatibility and its alternatives; what matters for this comparison is where PostgREST stops.

PostgREST has no notion of arbitrary business logic. Each rule must be expressed in SQL: an RPC function, a trigger, a constraint, an RLS policy. This is an accepted constraint, not an oversight — the diagram remains the only source of truth, which eliminates any drift between an application layer and the base it serves.

Concretely, it is impossible to call a third-party payment service, send a confirmation email, or calculate a score in JavaScript from a direct PostgREST request. This logic must either live in SQL (pl/pgsql function), or be triggered outside - a trigger which publishes an NOTIFYevent, listened to by an external service which is no longer PostgREST.

#
Comparison

Hasura: permissions declared, business logic grafted by webhook

Our article on GraphQL layers on Postgres details where the Hasura engine runs and how its permissions differ from the Postgres RLS. Here, the angle is that of business logic: how to plug custom code into a database managed by Hasura, and where.

A Action Hasura exposes a custom GraphQL mutation or query, backed by an HTTP webhook that you write in the language of your choice. Hasura validates inputs according to the declared schema, calls your webhook, then returns its response to the client. It is the gateway to any logic that goes beyond CRUD: call to a payment provider, complex calculation, multi-step orchestration.

The Event Triggers follow the opposite direction: an insert, an update or a delete on a table triggers a webhook, asynchronously and with automatic restart in the event of failure. This is the mechanism that most Hasura integrations use to synchronize a third-party service (billing, transactional email, search engine) without coupling this code to the initial customer request.

Hasura can also expose an already-written GraphQL query as a typical REST route, with a named path and parameters — its RESTifiedendpoints, in the terminology of its own documentation. Useful if your frontend team prefers to consume REST, without giving up the underlying GraphQL permissions engine.

A point worth repeating

Hasura permissions are a Hasura-specific system, per role and per table, not a delegation to the Postgres RLS. Two places to audit access rules rather than just one — a real cost to weigh against the flexibility gained from Actions and Event Triggers.

Context reminder, developed in our dedicated article: Hasura has refocused its communication on PromptQL, a layer designed for AI agents, since June 2025 — without removing its GraphQL engine, still presented as “battle-tested” on its official website.

#
Comparison

Custom API (Node.js, Express, Fastify): code everything, control everything

A hand-written API has, by definition, no limits: any business logic, in any language, with any dependencies. It's also the only one of the three options where nothing is generated for you — every route, every validation, every connection to the database is code that you own and must maintain.

routes/orders.js (Express, extrait)javascript
// The filter, sorting and relationship embedding are written by hand,
// for this route alone — repeat for each API resource
router.get('/orders', async (req, res) => {
  const { status } = req.query
  const result = await db.query(
    `SELECT o.id, o.total, c.email AS customer_email
     FROM orders o JOIN customers c ON c.id = o.customer_id
     WHERE o.status = $1 ORDER BY o.created_at DESC`,
    [status]
  );
  res.json(result.rows)
});

What this model offers, in exchange for manual work: full control over errors and returned HTTP codes, classic testability (handlers, not declarative configuration), and no new DSL to learn for a team that already masters its language.

What it costs, in exchange: CRUD, pagination and filters to write and maintain by hand for each resource; authentication and authorization to implement and audit yourself, without automatically inherited RLS; the risk of N+1 queries if each nested relationship triggers its own Postgres query without discipline; and API documentation to be maintained manually, or via a third-party generator to integrate.

On raw performance, the question “Is Node.js slower than Rust” is a topic in its own right — our article on Rust vs Node.js latency covers it in detail, with methodology declared upstream on our benchmark methodology page. A custom API has the same performance profile as any HTTP service you already use, neither better nor worse by construction. To know precisely where PostgREST saturates and at what point a custom layer becomes necessary, see our article on the real limits of PostgREST in production.

#
Decision

Comparison table: the three options side by side

Beyond the architecture, four criteria most often come up when choosing: speed of implementation, real business flexibility, long-term technical debt, and the typical use case where each option is most comfortable.

Initial setupMinutes — schema already existsHours — connect the base, configure permissionsDays to weeks — write each route
Business flexibilityLimited to SQL (RPC, triggers)Good via Actions/Event Triggers, but goes through an external webhookTotal, straightforward
Term technical debtWeak — the diagram remains the only source of truthMedium — Hasura metadata to maintain in addition to the schemaHigh if the team grows without discipline (tests, documentation, review)
Typical use caseDirect CRUD on a stable schema, team comfortable in SQLFederate several data sources, or AI agent-oriented logicComplex business logic, numerous third-party integrations
POSTGRESTHASURACUSTOM API
#
Checked in code

Where does business logic go on an Aurabase project

On an Aurabase Postgres engine project, the CRUD layer is already covered by an actual PostgREST instance, not an approximate reimplementation. The question that remains open for this comparison: where to write what goes beyond the CRUD?

Two paths exist, and they are not mutually exclusive. The first: an SQL function exposed in RPC, for any logic that remains reasonable to express in SQL — calculation of a total, cross-validation between several tables, cascading updates in a single transaction.

RPC call — business logic in SQLbash
curl -X POST "https://<gateway>/v1/db/<project_id>/rpc/apply_discount" \
  -H "apikey: <votre-cle-api>" \
  -H "Content-Type: application/json" \
  -d '{"order_id": 42, "code": "WELCOME10"}'

The second way: Edge Functions, for everything that goes beyond the SQL domain — calling a payment API, sending an email, calculating an embedding. Two paths lead there at Aurabase: the Studio editor, which runs Deno (TypeScript) code exactly as on Supabase, and the aura functions deployCLI, which aims for a separate path for functions written in Rust and compiled in WASM — detailed in our unified Rust architecture. Neither path requires hosting a separate Node server, unlike the pure “custom API” option in this article, where that server is entirely your responsibility.

This distribution is not a shaky compromise between the three models compared here: it is literally PostgREST for CRUD, a brick close to Hasura Actions for event logic via RPC and triggers, and Edge Functions which avoid the operation of a complete application server - without ever forcing a binary choice between "all PostgREST" and "all custom".

#
Decision

How to choose according to your context

Four situations come up most often. The right choice depends mostly on what your business logic requires, not on the popularity of a tool.

  • Your schema is stable and your business logic is in SQL. Self-hosted PostgREST, or natively integrated (Aurabase, Supabase), is enough: nothing more to host, and the schema remains the only source of truth.
  • You want to bring together several data sources, or your roadmap is geared towards AI agents consuming your data. Hasura, with its PromptQL layer, fits this terrain better.
  • Your product has rich business logic, numerous third-party integrations, and a team already equipped with an application language. A custom API remains the most direct choice, at the cost of writing it and maintaining it over time.
  • You want self-generated CRUD without giving up real space for business logic (RPC, Edge Functions), without stacking one more application service to exploit. This is the angle documented in this comparison applied to Aurabase, previous section.
#
Frequently Asked Questions

FAQs

Can PostgREST replace a custom Node.js API?+
For the CRUD layer, often yes. For any business logic that goes beyond what a SQL function can properly express, no: PostgREST has no notion of arbitrary business logic, unlike a custom API or Hasura Actions, which delegate this logic to application code.
Is Hasura open source?+
Hasura GraphQL Engine is released as open source. PromptQL, the layer designed for AI agents on which Hasura has refocused its communication since June 2025, is a separate product from this engine.
Can we combine PostgREST and a custom API on the same project?+
Yes, and this is a common pattern. PostgREST covers the standard CRUD exposed to the client, while a separate API or function handles sensitive operations (payment, email sending, multi-step logic) which then call the same Postgres database.
Does Aurabase offer Hasura integration?+
No. Aurabase integrates PostgREST natively for REST and pg_graphql optionally for GraphQL, not Hasura. The three approaches remain comparable on paper, but are not interchangeable on the platform.

READY TO DEPLOY?

Your backend in five minutes.

No credit card required · 500 MB free · 50,000 MAU