Prompt

How do I use JSONB in PostgreSQL for app settings and metadata?

Technology / Databases1 observationLast seen Oct 7, 2026

Latest observation

Oct 7, 2026OpenAI APIWeb search: off

PostgreSQL JSONB is a great fit for app settings, user preferences, and metadata when your data is semi-structured and changes over time.

When to use JSONB

Use it when:

  • Different rows can have different keys
  • You need flexible schema evolution
  • You want to store nested objects/arrays
  • You still want querying and indexing support

Avoid it when:

  • The data is highly relational and frequently joined
  • You need strict schema constraints on every field
  • The fields are core to your business logic and heavily updated individually at high scale

Basic table design

Example: storing settings and metadata for an app entity.

CREATE TABLE apps (
    id BIGSERIAL PRIMARY KEY,
    name TEXT NOT NULL,
    settings JSONB NOT NULL DEFAULT '{}'::jsonb,
    metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

Insert data

INSERT INTO apps (name, settings, metadata)
VALUES (
  'billing-service',
  '{"theme":"dark","notifications":{"email":true,"sms":false}}',
  '{"owner":"team-payments","version":"1.2.3","tags":["internal","critical"]}'
);

Querying JSONB

Access a top-level key

SELECT settings->>'theme' AS theme
FROM apps;

Access nested values

SELECT settings->'notifications'->>'email' AS email_notifications
FROM apps;

Filter by a JSONB value

SELECT *
FROM apps
WHERE settings->>'theme' = 'dark';

Check if a key exists

SELECT *
FROM apps
WHERE metadata ? 'owner';

Check if a JSON object contains another object

SELECT *
FROM apps
WHERE metadata @> '{"version":"1.2.3"}';

Updating JSONB

Update one key

Use jsonb_set:

UPDATE apps
SET settings = jsonb_set(settings, '{notifications,email}', 'false'::jsonb, true)
WHERE id = 1;

Add a new key

UPDATE apps
SET settings = jsonb_set(settings, '{language}', '"en"'::jsonb, true)
WHERE id = 1;

Merge objects

UPDATE apps
SET metadata = metadata || '{"environment":"prod"}'::jsonb
WHERE id = 1;

Indexing JSONB

JSONB can be slow without indexes if you query it often.

GIN index for containment queries

Good for @> and existence queries:

CREATE INDEX idx_apps_metadata_gin
ON apps
USING GIN (metadata);

Expression index for a specific field

If you frequently filter on one key:

CREATE INDEX idx_apps_theme
ON apps ((settings->>'theme'));

Partial index

Useful if only some rows use a field:

CREATE INDEX idx_apps_owner
ON apps ((metadata->>'owner'))
WHERE metadata ? 'owner';

Good practices

1. Keep frequently queried fields separate if needed

If you often query name, status, or tenant_id, store them in normal columns, not only in JSONB.

2. Use JSONB for flexible, optional, or evolving fields

Great for:

  • feature flags
  • UI preferences
  • integration metadata
  • external API payloads
  • audit/context data

3. Validate shape in the application or with constraints

You can add simple checks:

ALTER TABLE apps
ADD CONSTRAINT settings_must_be_object
CHECK (jsonb_typeof(settings) = 'object');

For stricter validation, use application validation or database triggers.

4. Default to empty objects

This avoids null handling:

settings JSONB NOT NULL DEFAULT '{}'::jsonb

5. Avoid storing huge, frequently rewritten blobs

Updating JSONB rewrites the whole value, so very large documents that change often can be inefficient.


Example: storing app settings and metadata together

CREATE TABLE user_profiles (
    user_id BIGINT PRIMARY KEY,
    preferences JSONB NOT NULL DEFAULT '{}'::jsonb,
    metadata JSONB NOT NULL DEFAULT '{}'::jsonb
);

Example values:

preferences:
{
  "theme": "dark",
  "language": "en",
  "notifications": {
    "email": true,
    "push": false
  }
}

metadata:
{
  "signup_source": "google",
  "last_login_ip": "203.0.113.42",
  "tags": ["beta", "premium"]
}

Rule of thumb

  • Relational columns for stable, important, heavily used fields
  • JSONB for flexible, optional, or evolving settings/metadata

If you want, I can also show:

  1. a real-world app settings schema,
  2. SQLAlchemy / Django / Node.js examples, or
  3. how to enforce JSON schema-like validation in PostgreSQL.

Observation history

How did Obsurfable measure this prompt?

Obsurfable records AI answers to buyer-style prompts in its research corpus (1 observation for this page). Metrics are distributions over observations, not a single static ranking.

Which AI systems does Obsurfable collect answers from?

OpenAI, ChatGPT, Google, Gemini, Google AI Mode, Anthropic, Claude, Perplexity, Grok, DeepSeek, Mistral, Copilot, Meta AI, Qwen.

How can brands improve their AEO?

Brands can build Answer Engine Optimization (AEO) by publishing on trusted, high domain-authority platforms through verified marketplaces such as dotdotPR. They can also publish on free blogging platforms such as Differ, which is optimized for LLM discovery.

Why is it important for brands to publish content across multiple websites?

Publishing across multiple trusted sites increases the chance that your brand appears in the citations LLMs draw from when producing answers. Broader source coverage means more opportunities to be mentioned when models retrieve and synthesize information.

Want this interpreted for your brand?

Explorer is the free public corpus. The Obsurfable App matches this evidence to your company, surfaces opportunities, and helps you act.