kysely-org / kysely-ctl

Command-line tool for Kysely
MIT License
50 stars 2 forks source link

required to install 'ws' to perform migrations from 'neondb' #10

Closed marcosrjjunior closed 1 month ago

marcosrjjunior commented 2 months ago

I'm required to install the 'ws' dependency into my project in order to perform migration on an external 'neondb' connection

error message:

> kysely migrate:down

◐ Starting migration down
✖ Migration failed with Error: All attempts to open a WebSocket to connect to the database failed. Please refer to https://github.com/neondatabase/serverless/blob/main/CONFIG.md#websocketconstructor-typeof-websocket--undefined. Details: fetch failed

temporary fix:

import { Pool, neonConfig } from '@neondatabase/serverless'
import ws from 'ws'

neonConfig.webSocketConstructor = ws

const pool = new Pool({ connectionString: process.env.DB_ADDRESS })

export default defineConfig({
  dialect: new PostgresDialect({ pool }),
  migrations: {
    migrationFolder: './src/lib/db/migrations',
  },
})

expected result: not having to install an ws dependency since the connection to run the project works just fine:

import { Kysely, PostgresDialect } from 'kysely'

const pool = new Pool({ connectionString: process.env.DB_ADDRESS })

export const db = new Kysely<...>({
  dialect: new PostgresDialect({ pool }),
})

running with previous migration-cli would work fine on this scenario.

we might be able to fix that by updating conditions on getDialect?

igalklebanov commented 1 month ago

Hey 👋

This is a no-go.

  1. It is documented in @neondatabase/serverless's readme. In runtimes that don't have global WebSocket, you'd have to provide ws or similar. For Node.js <22, you must provide ws, but for Node.js >=22, @neondatabase/serverless defaults to undici's web sockets implementation.
  2. esbuild doesn't support dynamically importing ws (which would dynamically import node:stream as a result). Can't just ship ws as a "hard" dependency and have it loaded all the time for a niche use case.
marcosrjjunior commented 1 month ago

Thanks for looking into to that @igalklebanov.

I partially understand the situation. It's interesting that it works just fine on kysely-migration-cli by passing the migrator with db dialect pointing to neon.

// db/migrate.ts
import { promises as fs } from 'fs'
import { Migrator, FileMigrationProvider } from 'kysely'
import { run } from 'kysely-migration-cli'
import path from 'path'

import { db } from '.'

const migrator = new Migrator({
  db,
  provider: new FileMigrationProvider({
    fs,
    path,
    migrationFolder: path.join(__dirname, './migrations'),
  }),
})

run(db, migrator, './src/lib/db/migrations')
// db/index.js
import { Pool } from '@neondatabase/serverless'
import { Kysely, PostgresDialect } from 'kysely'

const pool = new Pool({ connectionString: process.env.DB_ADDRESS })

export const db = new Kysely<...>({
  dialect: new PostgresDialect({ pool }),
})

I'm closing the issue.

igalklebanov commented 1 month ago

It's odd for sure.

Can you provide a reproduction repository? What node version?

@acro5piano any idea?

marcosrjjunior commented 1 month ago

the setup is basically the example mentioned above using the script

"db:migrate:up": "bun run -r dotenv/config ./src/lib/db/migrate latest",
"db:migrate:down": "ts-node -r dotenv/config ./src/lib/db/migrate down",

bun run or ts-node, both works

node version: v20.11.0

igalklebanov commented 1 month ago

bun has a built-in WebSocket implementation. https://bun.sh/docs/api/websockets#connect-to-a-websocket-server

I can't do anything without a reproduction.