PostgreSQL JSONB vs regular columns: when each option is worth it

Published on 2026-10-26 by frfrand

SQL modeling and queries on flexible fields without losing performance

PostgreSQL JSONB vs regular columns: when each option is worth it

In an API it's tempting to store an entire JSON object in a column and forget about the schema. PostgreSQL allows using JSONB and it's very useful, but when every query starts filtering inside huge documents it may be worth rethinking it.

The idea is to solve a data or deployment problem that appears in real applications. We'll see the concept straight to the point, an implementation example, and what to check before taking it to production.

What JSONB does vs normal columns

JSONB stores queryable and indexable binary documents, with specific operators. Relational columns offer types, keys, and more direct constraints for data you always need to filter, relate, or aggregate. It's not an absolute choice; they can coexist.

It's not enough to repeat a recipe if you don't understand who reads the data, from what process, and with what permissions. In an application with reservations, invoices, or users, a race condition or a poorly made access policy can be more serious than a slow API.

A concrete example

CREATE TABLE eventos (
  id bigserial PRIMARY KEY,
  cliente_id bigint NOT NULL,
  tipo text NOT NULL,
  metadata jsonb NOT NULL DEFAULT '{}'::jsonb
);
SELECT id, metadata->>'origen'
FROM eventos
WHERE tipo = 'reserva' AND metadata @> '{"origen":"web"}';
-- Evaluar índices según las consultas reales

The names, tables, and keys in this example are illustrative. Try it first in a test database or environment and adapt permissions, indexes, and versions. When there are write operations, do not run sample queries on customer data without reviewing what they modify.

Step by step

  1. Identify stable business fields such as client, date, amount, and status.
  2. Place those fields in typed columns when you need constraints and joins.
  3. Use JSONB for flexible metadata that changes between events.
  4. Measure real queries with EXPLAIN, and add appropriate indexes only when they help.
  5. Document JSON format and validations to avoid ending up with a hundred incompatible variants.

In the end, don't settle for "the SQL didn't error." Verify the actual effect from the API and with concurrency or permission tests as appropriate. In databases, there are errors that only appear when two users do something at the same time.

Example work project

A booking platform stores cliente_id, horario_id, and estado as columns, and uses JSONB for optional campaign source or preference attributes. This way it does not sacrifice booking integrity for flexibility.

That scenario helps distinguish performance from consistency. Speeding up a query is useless if it allows creating two bookings for a single slot, nor does a huge database fix the backend allowing querying another client's data.

The typical error

JSONB does not automatically replace foreign keys and constraints over the entire document. Nor do all JSONB queries become fast just because a GIN index exists.

It's also a good idea to have a recovery plan. Before touching schemas, users, or policies, save a verifiable copy or a reversible migration and check that the application can keep working during the change.

How would I solve this with ArduMaker?

When managing PostgreSQL in ArduMaker, mixing SQL types and JSONB wisely allows you to adapt the database without sacrificing frequent API queries.

ArduMaker allows you to deploy services from Git and work with databases and processes within the VPS. The panel does not replace your application's logic: transactions, permissions, and validations must be properly designed in the code and the database.

Three checks before publishing

Integrity: if a request is repeated or fails halfway through, do the data end up in a valid state?

Security: can an authenticated user access only their information, and does the API use a database identity with minimal permissions?

Recovery: do you know how to restore the data and revert the compatible code version if an update goes wrong?

If you can answer those three questions with tests, your backend is much better prepared than when you only checked if the endpoint returned 200.

Resources to learn more

To wrap up: choose the simplest solution that allows you to keep data correct, secure, and recoverable. Performance comes after measuring.

  • PostgreSQL
  • Tutorial
  • ArduMaker

On ArduMaker

  • PostgreSQL: Install PostgreSQL and manage databases, users and permissions without the command line.

Keep reading

All articles · VPS plans