kysely-planetscale
kysely-planetscale copied to clipboard
kysely-planetscale causes fetch to fail due to SSL version
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
})
});
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
})
});
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?
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.
Actually I think that's the same endpoint for mysql too, at least for my database - is that the hostname you're using?
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 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:
git clone https://github.com/vercel/nextjs-planetscale-nextauth-tailwindcss-template- Install dependencies with
pnpm install - Try to run it with
pnpm dev- note it failed here with thefetch failederror - Copied
.env.local.exampleto.env.localand added the database details:DATABASE_URL=mysql2://username:[email protected] - 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.
Running into the same problem using the local Planetscale CLI.
My Database URL DATABASE_URL=mysql://127.0.0.1/foo
@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.
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.
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:
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.
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.
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
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
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.