kysely-planetscale icon indicating copy to clipboard operation
kysely-planetscale copied to clipboard

kysely-planetscale causes fetch to fail due to SSL version

Open jgaltio opened this issue 2 years ago • 13 comments

Using this template: https://github.com/vercel/nextjs-planetscale-nextauth-tailwindcss-template

Fetch fails (this example is from a Canary version of Next.js but I tried numerous versions):

  ▲ Next.js 13.5.5-canary.2
  - Local:        http://localhost:3000
  - Environments: .env.local

 ✓ Ready in 3.7s
 ○ Compiling /page ...
 ✓ Compiled /page in 8.8s (1196 modules)
 ⨯ Internal error: TypeError: fetch failed
    at Object.fetch (node:internal/deps/undici/undici:11457:11)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
 ⨯ Internal error: TypeError: fetch failed
    at Object.fetch (node:internal/deps/undici/undici:11457:11)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
digest: "2026204822"

It seems the underlying issue is to do with how Kysely handles SSL...

  ] {
    library: 'SSL routines',
    reason: 'wrong version number',
    code: 'ERR_SSL_WRONG_VERSION_NUMBER'
  }

If I use mysql2 instead it works, however I wanted to use your lib like the template intends. Any idea what's going on with this or how to resolve it? It happens both locally and on Vercel.

This is how the library is being used:


import 'server-only';
import { Generated, Kysely } from 'kysely';
import { PlanetScaleDialect } from 'kysely-planetscale';

interface User {
  id: Generated<number>;
  name: string;
  username: string;
  email: string;
}

interface Database {
  users: User;
  // https://github.com/nextauthjs/next-auth/issues/4922
}

export const queryBuilder = new Kysely<Database>({
  dialect: new PlanetScaleDialect({
    url: process.env.DATABASE_URL
  })
});


jgaltio avatar Oct 05 '23 21:10 jgaltio

Hey! I'm not sure what would cause this specifically, likely something about the fetch implementation in your runtime environment or something that Next/Vercel are changing during the build.

It is possible to override the fetch implementation used by @planetscale/database though, see here: https://github.com/planetscale/database-js#custom-fetch-function.

The options passed to new PlanetScaleDialect({...}) are passed to the underlying @planetscale/database instance, so something like:

export const queryBuilder = new Kysely<Database>({
  dialect: new PlanetScaleDialect({
    url: process.env.DATABASE_URL,
    fetch: yourFetchOverride
  })
});

jacobwgillespie avatar Oct 06 '23 10:10 jacobwgillespie

Thanks much, Jacob. I gave that a good try with ChatGPT helping me, but even with different fetch providers it still seems to hit the SSL issue.

Is there a way I can / should specify the CA certificate Kysely like

import { Kysely } from 'kysely';
import fs from 'fs';
import path from 'path';

export const db = new Kysely({
  dialect: 'mysql',
  database: process.env.DATABASE_NAME,
  user: process.env.DATABASE_USERNAME,
  password: process.env.DATABASE_PASSWORD,
  host: process.env.DATABASE_HOST,
  port: 3306,
  ssl: {
    ca: fs.readFileSync(path.join(__dirname, 'certs', 'ca.crt')),
    cert: fs.readFileSync(path.join(__dirname, 'certs', 'cacert-2023-08-22.pem'))
  }
});

or

import fs from 'fs';
import path from 'path';
import { Generated, Kysely } from 'kysely';
import { PlanetScaleDialect } from 'kysely-planetscale';

// Only run this code if on the server
const isServer = typeof window === 'undefined';
let sslOptions = {};
if (isServer) {
  sslOptions = {
    ssl: {
      ca: fs.readFileSync(path.join(__dirname, 'certs', 'ca.crt')).toString(),
      cert: fs.readFileSync(path.join(__dirname, 'certs', 'cacert-2023-08-22.pem')).toString(),
      // ... any other required SSL options
    },
  };
}

interface User {
  id: Generated<number>;
  name: string;
  username: string;
  email: string;
}

interface Database {
  users: User;
}

export const queryBuilder = new Kysely<Database>({
  dialect: new PlanetScaleDialect({
    url: process.env.DATABASE_URL,
    ...sslOptions,
  }),
});

or something?

jgaltio avatar Oct 09 '23 18:10 jgaltio

I don't think it's supposed to work like that... one question, you said this is also happening locally: are you using the same DATABASE_URL for both mysql and @planetscale/database? The regular mysql endpoint does not work with PlanetScale's serverless driver, they have a different endpoint for that. I believe it is aws.connect.psdb.cloud for the DATABASE_HOST if your PlanetScale database is hosted in AWS.

jacobwgillespie avatar Oct 09 '23 20:10 jacobwgillespie

Actually I think that's the same endpoint for mysql too, at least for my database - is that the hostname you're using?

jacobwgillespie avatar Oct 09 '23 20:10 jacobwgillespie

Yep, mine looks like that and is hosted on AWS. The connection works fine if I'm using Planet Scale CLI or mysql command line. It's just the Next.js implementation that seems... impossible.

As of 2 hours ago I had to finally give up and pivot to a totally different approach but if there's a solution I'm happy to switch back.

jgaltio avatar Oct 09 '23 20:10 jgaltio

@jgaltio One other thing to check, are you specifying a port in your DATABASE_URL? If so, you should not include a port when using @planetscale/database, if you happen to have :3306 in there, that would also cause fetch to fail.

I was able to get that template to work by doing the following:

  1. git clone https://github.com/vercel/nextjs-planetscale-nextauth-tailwindcss-template
  2. Install dependencies with pnpm install
  3. Try to run it with pnpm dev - note it failed here with the fetch failed error
  4. Copied .env.local.example to .env.local and added the database details:
    DATABASE_URL=mysql2://username:[email protected]
    
  5. That worked for me (after creating the database table that the README mentioned)

If it's still broken and you happen to have a repo that reproduces the issue, I'm happy to take a look at that.

jacobwgillespie avatar Oct 09 '23 21:10 jacobwgillespie

Running into the same problem using the local Planetscale CLI. My Database URL DATABASE_URL=mysql://127.0.0.1/foo

jln13x avatar Oct 16 '23 16:10 jln13x

@jln13x that's not a valid database URL for the PlanetScale serverless driver (which uses HTTP rather than the MySQL protocol) - to my knowledge, the local PlanetScale CLI doesn't support the serverless driver like they do the MySQL one. There's an open issue tracking that here: https://github.com/planetscale/database-js/issues/110.

If you want to use Kysely with local MySQL / the PlanetScale CLI, the built-in Kysely MySQL dialect works for that.

jacobwgillespie avatar Oct 16 '23 20:10 jacobwgillespie

I thought I had tried with and without the port, but I will confirm. I tried local and remote MySQL but will focus on remote only now.

jgaltio avatar Oct 16 '23 22:10 jgaltio

Hey! +1 to what @jacobwgillespie said. Definitely don't include the port when using database-js. If you try to connect through a default MySQL port like 3306 today, you are going to have issues similar to this. Here's a similar issue in the database-js repo that might help out: https://github.com/planetscale/database-js/issues/142

I would recommend copying it directly from the UI, from Connect modal: CleanShot 2023-10-18 at 17 15 55@2x

Also, we actually include Kysely specific examples in our new onboarding. We don't reshow it in the UI again right now, but if you go to https://app.planetscale.com/org_name/database_name/connect and select Kysely you can see it.

tbarn avatar Oct 18 '23 22:10 tbarn

Tangentially related, but if you're interested in experimenting with something I've casually been working on:

https://github.com/mattrobenolt/ps-http-sim

This provides the HTTP shim that you can run locally to pair with a local MySQL database.

mattrobenolt avatar Oct 19 '23 01:10 mattrobenolt

I came across the same problem. I found out that when you directly paste the content of your DATABASE_URL like this:

new Kysely<Database>({ 
  dialect: new PlanetScaleDialect({
    url: 'mysql://o8jh6w53y.....horized":true}',
    format: SqlString.format
  })
});

It works as expected. But when you use process.env.DATABASE_URL it doesn't. Weird.

It looks like the process.env.DATABASE_URL is not available in the edge lambda console.log({ DATABASE_URL: process.env.DATABASE_URL, url }); logged

{
  DATABASE_URL: undefined,
  url: 'mysql://o8jh6w5...thorized":true}'
}

That's the problem we are facing

rafalfigura avatar Feb 04 '24 19:02 rafalfigura

Found a way to fix that issue. Take a look at this: https://qwik.dev/docs/env-variables/#accessing-the-environment-variables-in-serverfull-architecture-exampleexpress

rafalfigura avatar Feb 04 '24 20:02 rafalfigura

I'm going to close this issue since SSL errors are usually caused either by an incorrect server URL value, or the runtime environment, not this driver directly.

jacobwgillespie avatar May 08 '24 09:05 jacobwgillespie