technicalarchitecture

Why Your Form Data Deserves a Real Database

JSON blobs vs relational tables: why we chose PostgreSQL as the backbone of RoundPushPin and what it means for your data.

R
RoundPushPin Team
Why Your Form Data Deserves a Real Database

Most form builders store your responses as JSON documents. It works — until you need to actually do something with that data. Here's why we chose a relational model instead.

Diagram comparing a JSON blob and relational tables for storing the same form response

Why are JSON blobs a problem for form data?

JSON blobs are easy to store but hard to use: they offer no referential integrity, no cross-form queries, no type safety, and no efficient indexing. As soon as you need to analyze, join, or validate responses, the unstructured format works against you.

When a user submits a form on most platforms, the response gets serialized into something like this:

{
  "form_id": "abc123",
  "responses": {
    "name": "Jane Doe",
    "email": "[email protected]",
    "role": "Engineer",
    "experience": "5-10 years"
  },
  "submitted_at": "2026-01-28T10:00:00Z"
}

This is easy to store. It's flexible. But it has serious limitations:

  1. No referential integrity. Delete a question and orphaned data floats around forever.
  2. No cross-form queries. Want the average completion time grouped by question type across all forms? You need to deserialize every response.
  3. No type safety. The "experience" field could contain anything — a number, a string, an array.
  4. No indexing. You can't efficiently query "all responses where role = Engineer" without scanning every document.

How does a relational model store form data?

In a relational model, every form maps to a structured PostgreSQL schema: each question becomes a typed column and each response becomes a row. Foreign keys keep everything connected, so nothing gets orphaned and every field has a known type.

In RoundPushPin, the same data looks like this:

-- Forms table
SELECT * FROM forms WHERE id = 'abc123';

-- Blocks (questions) with enforced types
SELECT * FROM blocks WHERE form_id = 'abc123' ORDER BY position;

-- Responses with proper columns
SELECT name, email, role, experience
FROM responses_abc123
WHERE submitted_at > '2026-01-01';

Each form gets a structured schema. Each question maps to a typed column. Foreign keys ensure nothing gets orphaned.

What does relational form storage enable?

With relational storage, you can:

  • Run analytics directly: SELECT role, COUNT(*) FROM responses GROUP BY role
  • Join with other data: Connect form responses to your user table, CRM, or any other system
  • Enforce constraints: Required fields, unique values, valid email formats — at the database level
  • Version your schema: Track changes to form structure over time with migrations

What's the trade-off of a relational schema?

Relational schemas are less flexible than JSON. Adding a question means adding a column. But we think this trade-off is worth it. The discipline of a structured schema pays dividends in data quality, queryability, and long-term maintainability — a principle at the heart of our product vision.

Your form data isn't throwaway. It deserves a real database.

Frequently asked questions

Why use a relational database for form responses instead of JSON?
Relational tables enforce types and relationships and let you query and join responses with SQL, while JSON blobs store everything in one document you must deserialize to analyze. For data you'll actually use, relational wins on integrity and queryability.
Can you store form data in PostgreSQL?
Yes. PostgreSQL suits form data well: each question becomes a typed column, constraints enforce required and format rules, and you query responses with standard SQL. RoundPushPin maps every form to a PostgreSQL schema automatically.
What's wrong with storing form responses as JSON blobs?
JSON blobs lack referential integrity, type safety, and efficient indexing, so you can't easily validate, join, or query responses without deserializing every document. They're flexible but become a liability once you need to analyze the data.

Sources

  1. JSON Types — PostgreSQL Global Development Group
  2. Constraints — PostgreSQL Global Development Group
  3. Drizzle ORM — Overview — Drizzle
<