Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/busy-beans-change.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@effect/sql-drizzle": minor
---

Fix JSONB deserialization for db.insert in Effect + Drizzle integration
15 changes: 14 additions & 1 deletion packages/sql-drizzle/src/internal/patch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,20 @@ export const makeRemoteCallback = Effect.gen(function*() {
const constructionRuntime = yield* Effect.runtime<never>()
return (sql: string, params: Array<any>, method: "all" | "execute" | "get" | "values" | "run") => {
const runPromise = Runtime.runPromise(currentRuntime ? currentRuntime : constructionRuntime)
const statement = client.unsafe(sql, params)

const finalParams = params.map((e) => {
if (e === null || e === undefined) return e
if (typeof e !== "string") return e
const s = e.trim()
if (!(s.startsWith("{") || s.startsWith("["))) return e
Copy link
Contributor

@tim-smart tim-smart Sep 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this work if you have a non-json param that starts with these characters?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @tim-smart , yes if a non-JSON param happens to start with { or [, it will attempt JSON.parse. Since parsing will fail, it falls into the catch block and simply returns the original string unchanged.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I don't think this is robust enough to merge sorry. I'm not very familiar with drizzle, but you would need to somehow identify params that are only for json columns.

try {
return JSON.parse(s)
} catch {
return e
}
})

const statement = client.unsafe(sql, finalParams)
if (method === "execute") {
return runPromise(Effect.either(Effect.map(statement.raw, (header) => ({ rows: [header] })))).then((res) => {
if (res._tag === "Left") {
Expand Down
26 changes: 26 additions & 0 deletions packages/sql-drizzle/test/Pg.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,30 @@ describe.sequential("Pg", () => {
Effect.catchTag("ContainerError", () => Effect.void)
)
}, { timeout: 60000 })

it.effect("supports object params (auto JSON serialization)", () =>
Effect.gen(function*() {
const sql = yield* SqlClient.SqlClient
const db = yield* Pg.PgDrizzle

yield* sql`CREATE TABLE data_objects (id SERIAL PRIMARY KEY, payload JSONB NOT NULL)`

const payload = { foo: "bar", count: 42 }

yield* db.insert(D.pgTable("data_objects", {
id: D.serial("id").primaryKey(),
payload: D.jsonb("payload").notNull()
})).values({ payload })

const rows = yield* sql`SELECT id, payload FROM data_objects ORDER BY id`

assert.deepStrictEqual(rows, [
{ id: 1, payload }
])
}).pipe(
Effect.provide(DrizzlePgLive),
Effect.catchTag("ContainerError", () => Effect.void)
), {
timeout: 60000
})
})