drizzle-team / drizzle-orm

Headless TypeScript ORM with a head. Runs on Node, Bun and Deno. Lives on the Edge and yes, it's a JavaScript ORM too 😅
https://orm.drizzle.team
Apache License 2.0
24.68k stars 651 forks source link

[BUG]: Typescript error with returning({...}) function when combining both HTTP and WS Neon connection types #3334

Open bfovez opened 3 weeks ago

bfovez commented 3 weeks ago

What version of drizzle-orm are you using?

0.36.0

What version of drizzle-kit are you using?

0.27.0

Describe the Bug

My use case is the following: I'm building a library dealing with a Neon database, but on a database with is managed by the app using the library. So, all the functions of the lib expects as a their first parameter the actual connection to the database. This connection is created within the main app with two different flavors: HTTP driver or Websockets driver. Like so:

Neon HTTP driver

// Setting up a connection with Neon HTTP driver
// See the Drizzle doc:
//  - https://orm.drizzle.team/docs/connect-neon page
//  - Section "Step 2 - Initialize the driver and make a query"
//  - Tab "Neon HTTP"
import { drizzle } from 'drizzle-orm/neon-http' // <-- neon-http here
const db = drizzle(url)

Neon Websockets driver

// Setting up a connection with Neon HTTP driver
// See the Drizzle doc:
//  - https://orm.drizzle.team/docs/connect-neon page
//  - Section "Step 2 - Initialize the driver and make a query"
//  - Tab "Neon Websockets"
import { drizzle } from 'drizzle-orm/neon-serverless' // <-- neon-serverless here
import ws from 'ws'
export const db = drizzle({ connection: url, ws: ws })

Neon HTTP driver is faster, but does not support transactions. Neon WS driver does support transaction but slower. In order to take advantages of both world, the lib should required the WS flavor for only the function which do need a transaction, and should accept BOTH flavors for the others. In order to type the db to be passed,

But I came across some weird behavior with the returning({ ... }) function: it seems that the two flavors can't be or-ed in a type.

Here is the full code to reproduce:

import dotenv from 'dotenv'
import { drizzle as drizzleHttp } from 'drizzle-orm/neon-http'
import { drizzle as drizzleWs } from 'drizzle-orm/neon-serverless'
import { pgTable, text, uuid } from 'drizzle-orm/pg-core'
import ws from 'ws'

dotenv.config()
const url = process.env.DATABASE_URL
if (!url) throw new Error('DATABASE_URL is not set')

// Setting up a connection with Neon HTTP driver
// See the Drizzle doc:
//  - https://orm.drizzle.team/docs/connect-neon page
//  - Section "Step 2 - Initialize the driver and make a query"
//  - Tab "Neon HTTP"
export const dbWithHttpDriver = drizzleHttp(url)
type DB_WITH_HTTP_DRIVER = typeof dbWithHttpDriver

// Setting up a connection with Neon HTTP driver
// See the Drizzle doc:
//  - https://orm.drizzle.team/docs/connect-neon page
//  - Section "Step 2 - Initialize the driver and make a query"
//  - Tab "Neon Websockets"
export const dbWithWsDriver = drizzleWs({ connection: url, ws: ws })
type DB_WITH_WS_DRIVER = typeof dbWithWsDriver

// Defining a simple test table
const testTable = pgTable('test_table', {
    id: uuid().primaryKey().defaultRandom(),
    someField: text(),
})

// Inserting with HTTP driver: works fine
export async function createWithHttp(
    db: DB_WITH_HTTP_DRIVER, // <-- Accept one HTTP flavor only
    someField: string,
) {
    const { id } = (await db.insert(testTable).values({ someField }).returning({ id: testTable.id }))[0]!
    console.log(id)
}

// Inserting with HTTP driver: works fine
export async function createWithWs(
    db: DB_WITH_HTTP_DRIVER, // <-- Accept one WS flavor only
    someField: string,
) {
    const { id } = (await db.insert(testTable).values({ someField }).returning({ id: testTable.id }))[0]!
    console.log(id)
}

// Inserting with either HTTP or WS driver: typescript error!
export async function createWithHttpOrWs(
    db: DB_WITH_HTTP_DRIVER | DB_WITH_WS_DRIVER, // <-- Accept both HTTP and WS flavors
    someField: string,
) {
    const { id } = (await db.insert(testTable).values({ someField }).returning({ id: testTable.id }))[0]!
    // Red squiggling here --------------------------------------------------------------^
    //                                                    Expected 0 arguments, but got 1.ts(2554)
    //                                                    ⚠ Error(TS2554)  |
    //                                                    Expected 0 arguments, but got 1.
    console.log(id)
}

Is something from the Drizzle team can help to figure with out? Is there a type to be used when you want to pass the connection to a function?

Many thanks in advance!

Expected behavior

No typescript error.

Environment & setup

Node.js v20.11.1

nacho-vazquez commented 2 weeks ago

I'm getting this with a different flavor of Postgress, RDS Data API, so I don't think it has less to do with Neon and more with the typescript definition of the insert + returning.