porsager / porsager/postgres

JSONB double-encoding when used with Drizzle ORM

Open
#1,139 3 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
8.7k
Forks
374
Avg merge
11d 16h
Merged PRs (30d)
1

Description

Environment

  • postgres: 3.4.0
  • drizzle-orm: 0.30.0
  • PostgreSQL: 15

Issue

When Drizzle ORM passes pre-stringified JSONB values, postgres-js stringifies them again, causing double-encoding.

// Drizzle passes: '{"foo":"bar"}'
// postgres-js serializes to: '"{\\"foo\\":\\"bar\\"}"'
// PostgreSQL stores: string instead of jsonb object

Root cause

postgres-js assumes all JSONB values need serialization:

// src/types.js
json: {
  to: 114,
  from: [114, 3802],  // 3802 = jsonb
  serialize: x => JSON.stringify(x),
  parse: x => JSON.parse(x)
}

However, ORMs like Drizzle pre-process values:

// Drizzle's PgJsonb.mapToDriverValue()
override mapToDriverValue(value: T['data']): string {
  return JSON.stringify(value);  // Already stringified
}

Result: JSON.stringify(JSON.stringify(value))

Reproduction

const { drizzle } = require('drizzle-orm/postgres-js')
const { pgTable, text, jsonb } = require('drizzle-orm/pg-core')
const postgres = require('postgres')

const client = postgres('postgresql://...')
const db = drizzle(client)

const test = pgTable('test', {
  id: text('id').primaryKey(),
  data: jsonb('data')
})

await db.insert(test).values({ id: '1', data: { foo: 'bar' } })

// SELECT jsonb_typeof(data) FROM test;
// Expected: "object"
// Actual: "string"

Workaround

Override type configuration:

const client = postgres(url, {
  types: {
    json: {
      to: 114,
      from: [114, 3802],
      serialize: x => x,  // Pass through
      parse: x => JSON.parse(x)
    }
  }
})

Suggestion

Check if value is already a string:

json: {
  to: 114,
  from: [114, 3802],
  serialize: x => typeof x === 'string' ? x : JSON.stringify(x),
  parse: x => JSON.parse(x)
}

This works for both ORM and direct usage scenarios.

Impact

Affects all ORMs that pre-process JSONB values (Drizzle, Prisma, TypeORM).

Related

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with src/types.js and the provided Drizzle ORM reproduction, then inspect the existing JSON/JSONB type configuration. Verify behavior for both pre-stringified ORM values and direct JavaScript values; done means PostgreSQL stores the inserted JSONB as an object rather than a string without breaking direct usage.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, nodejs, postgresql
Domain
backend, databases
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.