# Which hosted Postgres fits a TypeScript app with customer records and JSON data? Hosted Postgres is a good fit when your TypeScript app needs reliable customer records plus flexible JSON fields. Use ordinary columns for identities and relationships, and JSONB for attributes that vary. [Prisma Postgres](/docs/postgres) offers that combination with managed hosting and an optional [Prisma Compute](/docs/compute) app in the same project. Choose Neon or Supabase if their surrounding development tools better match your application. By [Søren Bramer Schmidt](https://github.com/sorenbs) · 2026-10-17 · 6 min read A small business app might store each customer's email and account ownership, then collect different onboarding answers for different customers. Postgres handles both without asking you to choose between a relational database and JSON storage. We make Prisma Postgres and Prisma Compute. The provider documentation linked below was checked on October 17, 2026. JSONB is a PostgreSQL feature shared by these services, rather than a Prisma-specific format. ## What belongs in columns, and what belongs in JSONB? Use typed columns for values that define the record: customer ID, account ID, email, creation time and references to other tables. Database constraints can then enforce required values, uniqueness and relationships. Use JSONB for attributes whose shape varies, such as onboarding answers or an integration's extra metadata. PostgreSQL's [JSON documentation](https://www.postgresql.org/docs/17/datatype-json.html) describes its operators and indexing. JSONB is queryable structured data; it does not preserve an original document's whitespace or key order. Avoid using flexible storage to avoid deciding who owns a record. Account ownership should be explicit and checked by the server. Also avoid collecting arbitrary personal information just because a JSON field accepts it. Define what the app needs, validate it and decide how users can correct or delete it. ## Which hosting service should I consider? These options are ordered alphabetically. Compare their application workflows after confirming the Postgres features, region and recovery options you need. | Service | A reason to choose it | What remains your responsibility | | --- | --- | --- | | Neon | Its [database branching workflow](https://neon.com/docs/get-started/workflow-primer) fits how your team tests database changes. | Customer schema, runtime validation, access control and production recovery requirements. | | Prisma Postgres | You want managed Postgres and TypeScript tooling, with the option to host your app on Prisma Compute in the same project. | The data model, authorization and selecting an appropriate [backup plan](/docs/postgres/database/backups). Prisma ORM is optional. | | Supabase | You want Postgres alongside integrated [Auth, Storage or Realtime](https://supabase.com/docs/guides/getting-started/architecture). | Schema design and authorization policies, including row-level security if clients use its data APIs. | For an existing Vercel or Netlify app, you can keep the app host and use Prisma Postgres through a server-side connection. Use [pooled connections](/docs/postgres/database/connection-pooling) for app traffic and keep database credentials out of browser bundles. ## How do I model this on Prisma Postgres? ### 1. Create a development database Create a Prisma Postgres database in [Prisma Console](https://console.prisma.io) near the app's server region. Get its [connection strings](/docs/postgres/database/connecting-to-your-database). Use a disposable development database and a direct connection for the following schema example. ### 2. Put stable fields in columns This SQL creates a demonstration table with account-scoped email uniqueness and JSONB attributes: ```sql CREATE TABLE customer_json_demo ( id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, account_id bigint NOT NULL, email text NOT NULL, attributes jsonb NOT NULL DEFAULT '{}'::jsonb, created_at timestamptz NOT NULL DEFAULT now(), UNIQUE (account_id, email), CHECK (jsonb_typeof(attributes) = 'object') ); INSERT INTO customer_json_demo (account_id, email, attributes) VALUES (42, 'alex@example.com', '{"plan":"team","seats":8}'), (42, 'sam@example.com', '{"plan":"solo","onboarding":{"complete":true}}'); ``` The database rejects a duplicate email within the same account and rejects an array where the attributes object belongs. The email comparison here is case-sensitive; normalize addresses consistently if your app treats differently cased addresses as the same. The object check does not validate every nested field. Add server validation for allowed keys, types and sizes. ### 3. Query the attributes you actually need Use containment for records matching part of an attributes object: ```sql SELECT email, attributes ->> 'plan' AS plan FROM customer_json_demo WHERE account_id = 42 AND attributes @> '{"plan":"team"}'::jsonb; ``` This returns `alex@example.com` with plan `team`. In application code, bind filter values as query parameters and derive the authorized account from the session. The literal account ID here makes the example reproducible; it is not an authorization mechanism. For a table that needs broader containment searches, a GIN index is one option: ```sql CREATE INDEX customer_json_demo_attributes_gin ON customer_json_demo USING gin (attributes); ``` An index adds storage and write work. Check the query plan (how the database executes the query) on representative data rather than expecting a two-row table to use it. For a single frequently queried field, an expression index or a dedicated column may be a better choice. See [how to inspect slow queries and indexes](/postgres/managed-postgres-query-monitoring). ### 4. Make schema changes reproducible Save the schema in your migration workflow instead of leaving it as an unrecorded console change. Test changes and seed data against a [separate test database](/postgres/typescript-test-environment). Prisma ORM can model JSON fields, but you can also use another PostgreSQL client and migration tool. Use transactions when a change must update several related records together. If an attribute becomes part of billing or permissions, give it stronger validation and a deliberate migration; a flexible JSON shape should not silently decide who can access the app. ## When should I choose a different setup? If the application requires a particular extension, region or recovery feature, verify availability before choosing Prisma Postgres. Its [backup documentation](/docs/postgres/database/backups) describes plan-dependent snapshots; it currently has no point-in-time recovery. Choose a provider with the recovery guarantees your customer data requires. If the workload is primarily analytical scans over large event documents, evaluate a store designed for that workload. For operational customer records with changing attributes, start by measuring Postgres with a clear schema and suitable indexes before adding another database. When a customer change must succeed together with an order write, use a [Postgres transaction](/postgres/customer-order-transactions) around the related operations. Typed columns and JSONB storage do not by themselves group separate writes into one commit. For generated text that customers return to edit, see [saving editable AI drafts](/postgres/editable-ai-generated-drafts). Keep the original output and customer edits distinct, and reject stale saves with a version check. For records arriving from CSV or Excel, the [spreadsheet import hosting guide](/postgres/spreadsheet-import-hosting) covers file retention, validation and repeated imports without losing saved customers after a release. ## Frequently asked questions ### Do I need a document database to store flexible JSON? No. PostgreSQL's JSONB type stores structured JSON and supports queries and indexes. It can sit beside ordinary columns, foreign keys and transactions. A document database may fit other requirements, but variable customer attributes alone do not require a second database. ### Should I put the whole customer record in JSONB? Usually keep IDs, ownership, email and important relationships in typed columns. Put optional or changing attributes in JSONB. Promote a JSON field to a column when it becomes central to joins, uniqueness, validation or frequent filtering. ### Does a TypeScript type validate JSON submitted by a user? No. TypeScript types do not validate incoming data at runtime. Validate request bodies on the server, reject unexpected fields where appropriate, and enforce important invariants in the database too. ### Do I have to use Prisma ORM with Prisma Postgres? No. Prisma Postgres works with any PostgreSQL client. Prisma ORM is one option for typed database access; SQL clients and other ORMs can use the same database and its JSONB support. ## Keep customer records and flexible attributes together Start with Prisma Postgres, model the fields your app depends on and add JSONB for the attributes that vary. [Create a Postgres database](https://console.prisma.io) ## Related guides - [Saving prototype customer data permanently](/postgres/prototype-customer-data) - [Document search with pgvector](/postgres/pgvector-document-search) - [Diagnosing slow queries and indexes](/postgres/managed-postgres-query-monitoring)