Skip to content

JSONB double-encoding when used with Drizzle ORM #1139

@zxzinn

Description

@zxzinn

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions