N
Neon2y ago
ambitious-aqua

How do I properly use the serverless driver for prisma?

accidentally deleted original post... tried to edit lmao my bad. nextjs 14 nextauth v5 prisma 5.13 neon serverless driver package.json details are below getting a ws error:
[auth][cause]: 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: ws
does not work in the browser. Browser clients must use the native WebSocket object
[auth][cause]: 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: ws
does not work in the browser. Browser clients must use the native WebSocket object
I originally used the JWT session strategy but wanted to try the database session to experiment but also because I thought it would fix this issue. JWT session: When I attempt to access my database from the JWT or session callback in nextauth I get the error. I was hoping I could try to get updated user data in the session without them having to login. Database session: I get the error on every page refresh, and on every sign in attempt. Also when I sign in it seems successful but keeps me on the sign in page and does not redirect. If I go to the home page I see there is some sort of session that is working as the user menu shows my name + email but it does not show the providers image for some reason. if I go to the settings page after this it will redirect me to back to the login page
69 Replies
ambitious-aqua
ambitious-aquaOP2y ago
auth.ts:
import { PrismaAdapter } from "@auth/prisma-adapter";
import bcrypt from "bcryptjs";
// import { compareSync } from "bcrypt-edge";
import NextAuth from "next-auth";
import Credentials from "next-auth/providers/credentials";
import GitHub from "next-auth/providers/github";
import Google from "next-auth/providers/google";

import { env } from "~/env";
import { LoginSchema } from "~/schemas/auth";
import {
deleteTwoFactorConfirmation,
getTwoFactorConfirmationByUserId,
} from "~/server/data-access/2fa-confirmation";
import { findUserByEmail, findUserById } from "~/server/data-access/user";
import { db } from "~/server/db";
import { updateUserEmailVerified } from "~/server/use-cases/user";

export const { handlers, signIn, signOut, auth } = NextAuth({
adapter: PrismaAdapter(db),
providers: [
Google,
GitHub,
Credentials({
async authorize(credentials) {
const validatedFields = LoginSchema.safeParse(credentials);
if (validatedFields.success) {
const { email, password } = validatedFields.data;

try {
const user = await findUserByEmail(email);
if (!user?.password) {
return null;
}

const isPasswordCorrect = await bcrypt.compare(
password,
user.password,
);
if (isPasswordCorrect) {
return user;
}
} catch (error) {
console.error("Error authorizing user credentials:", error);
throw new Error("Error authorizing user credentials");
}
}

return null;
},
}),
],
session: {
strategy: "database",
},
callbacks: {
async signIn({ user, account }) {
if (account?.provider !== "credentials") {
return true;
}

const existingUser = await findUserById(user.id!);
if (!existingUser?.emailVerified) {
return false;
}

if (existingUser.isTwoFactorEnabled) {
const twoFactorConfirmation = await getTwoFactorConfirmationByUserId(
user.id!,
);

if (!twoFactorConfirmation) {
return false;
}

await deleteTwoFactorConfirmation(user.id!);
}

return true;
},
async session({ session, user }) {
session.user.role = user.role;
session.user.isTwoFactorEnabled = user.isTwoFactorEnabled;

return session;
},
},
pages: {
signIn: "/auth/login",
error: "/auth/error",
},
events: {
async linkAccount({ user }) {
await updateUserEmailVerified(user.id!);
},
},
debug: env.NODE_ENV !== "production",
});
import { PrismaAdapter } from "@auth/prisma-adapter";
import bcrypt from "bcryptjs";
// import { compareSync } from "bcrypt-edge";
import NextAuth from "next-auth";
import Credentials from "next-auth/providers/credentials";
import GitHub from "next-auth/providers/github";
import Google from "next-auth/providers/google";

import { env } from "~/env";
import { LoginSchema } from "~/schemas/auth";
import {
deleteTwoFactorConfirmation,
getTwoFactorConfirmationByUserId,
} from "~/server/data-access/2fa-confirmation";
import { findUserByEmail, findUserById } from "~/server/data-access/user";
import { db } from "~/server/db";
import { updateUserEmailVerified } from "~/server/use-cases/user";

export const { handlers, signIn, signOut, auth } = NextAuth({
adapter: PrismaAdapter(db),
providers: [
Google,
GitHub,
Credentials({
async authorize(credentials) {
const validatedFields = LoginSchema.safeParse(credentials);
if (validatedFields.success) {
const { email, password } = validatedFields.data;

try {
const user = await findUserByEmail(email);
if (!user?.password) {
return null;
}

const isPasswordCorrect = await bcrypt.compare(
password,
user.password,
);
if (isPasswordCorrect) {
return user;
}
} catch (error) {
console.error("Error authorizing user credentials:", error);
throw new Error("Error authorizing user credentials");
}
}

return null;
},
}),
],
session: {
strategy: "database",
},
callbacks: {
async signIn({ user, account }) {
if (account?.provider !== "credentials") {
return true;
}

const existingUser = await findUserById(user.id!);
if (!existingUser?.emailVerified) {
return false;
}

if (existingUser.isTwoFactorEnabled) {
const twoFactorConfirmation = await getTwoFactorConfirmationByUserId(
user.id!,
);

if (!twoFactorConfirmation) {
return false;
}

await deleteTwoFactorConfirmation(user.id!);
}

return true;
},
async session({ session, user }) {
session.user.role = user.role;
session.user.isTwoFactorEnabled = user.isTwoFactorEnabled;

return session;
},
},
pages: {
signIn: "/auth/login",
error: "/auth/error",
},
events: {
async linkAccount({ user }) {
await updateUserEmailVerified(user.id!);
},
},
debug: env.NODE_ENV !== "production",
});
package.json dependencies:
"dependencies": {
"@auth/prisma-adapter": "^2.0.0",
"@hookform/resolvers": "^3.3.4",
"@neondatabase/serverless": "^0.9.1",
"@paralleldrive/cuid2": "^2.2.2",
"@plaiceholder/next": "^3.0.0",
"@prisma/adapter-neon": "^5.13.0",
"@prisma/client": "^5.13.0",
"@radix-ui/react-alert-dialog": "^1.0.5",
"@radix-ui/react-avatar": "^1.0.4",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-navigation-menu": "^1.1.4",
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-progress": "^1.0.3",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-separator": "^1.0.3",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-tabs": "^1.0.4",
"@radix-ui/react-toast": "^1.1.5",
"@radix-ui/react-toggle": "^1.0.3",
"@radix-ui/react-toggle-group": "^1.0.4",
"@radix-ui/react-tooltip": "^1.0.7",
"@t3-oss/env-nextjs": "^0.9.2",
"@uploadthing/react": "^6.5.1",
"bcrypt-edge": "^0.0.3",
"bcryptjs": "^2.4.3",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.0",
"cmdk": "^1.0.0",
"dotenv": "^16.4.5",
"lucide-react": "^0.368.0",
"next": "^14.1.4",
"next-auth": "^5.0.0-beta.17",
"next-superjson-plugin": "0.6.1",
"next-themes": "^0.3.0",
"nextjs-toploader": "^1.6.12",
"plaiceholder": "^3.0.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-hook-form": "^7.51.3",
"resend": "^3.2.0",
"sharp": "^0.33.3",
"slug": "^9.0.0",
"sonner": "^1.4.41",
"superjson": "2.2.1",
"tailwind-merge": "^2.2.2",
"tailwindcss-animate": "^1.0.7",
"uploadthing": "^6.10.1",
"use-debounce": "^10.0.0",
"ws": "^8.17.0",
"zod": "^3.22.5"
},
"devDependencies": {
"@ianvs/prettier-plugin-sort-imports": "^4.2.1",
"@types/bcryptjs": "^2.4.6",
"@types/eslint": "^8.56.2",
"@types/node": "^20.11.20",
"@types/react": "^18.2.57",
"@types/react-dom": "^18.2.19",
"@types/slug": "^5.0.8",
"@types/ws": "^8.5.10",
"@typescript-eslint/eslint-plugin": "^7.1.1",
"@typescript-eslint/parser": "^7.1.1",
"bufferutil": "^4.0.8",
"eslint": "^8.57.0",
"eslint-config-next": "^14.1.3",
"postcss": "^8.4.34",
"prettier": "^3.2.5",
"prettier-plugin-tailwindcss": "^0.5.11",
"prisma": "^5.13.0",
"tailwindcss": "^3.4.1",
"tsx": "^4.7.2",
"typescript": "^5.4.2"
},
"dependencies": {
"@auth/prisma-adapter": "^2.0.0",
"@hookform/resolvers": "^3.3.4",
"@neondatabase/serverless": "^0.9.1",
"@paralleldrive/cuid2": "^2.2.2",
"@plaiceholder/next": "^3.0.0",
"@prisma/adapter-neon": "^5.13.0",
"@prisma/client": "^5.13.0",
"@radix-ui/react-alert-dialog": "^1.0.5",
"@radix-ui/react-avatar": "^1.0.4",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-navigation-menu": "^1.1.4",
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-progress": "^1.0.3",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-separator": "^1.0.3",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-tabs": "^1.0.4",
"@radix-ui/react-toast": "^1.1.5",
"@radix-ui/react-toggle": "^1.0.3",
"@radix-ui/react-toggle-group": "^1.0.4",
"@radix-ui/react-tooltip": "^1.0.7",
"@t3-oss/env-nextjs": "^0.9.2",
"@uploadthing/react": "^6.5.1",
"bcrypt-edge": "^0.0.3",
"bcryptjs": "^2.4.3",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.0",
"cmdk": "^1.0.0",
"dotenv": "^16.4.5",
"lucide-react": "^0.368.0",
"next": "^14.1.4",
"next-auth": "^5.0.0-beta.17",
"next-superjson-plugin": "0.6.1",
"next-themes": "^0.3.0",
"nextjs-toploader": "^1.6.12",
"plaiceholder": "^3.0.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-hook-form": "^7.51.3",
"resend": "^3.2.0",
"sharp": "^0.33.3",
"slug": "^9.0.0",
"sonner": "^1.4.41",
"superjson": "2.2.1",
"tailwind-merge": "^2.2.2",
"tailwindcss-animate": "^1.0.7",
"uploadthing": "^6.10.1",
"use-debounce": "^10.0.0",
"ws": "^8.17.0",
"zod": "^3.22.5"
},
"devDependencies": {
"@ianvs/prettier-plugin-sort-imports": "^4.2.1",
"@types/bcryptjs": "^2.4.6",
"@types/eslint": "^8.56.2",
"@types/node": "^20.11.20",
"@types/react": "^18.2.57",
"@types/react-dom": "^18.2.19",
"@types/slug": "^5.0.8",
"@types/ws": "^8.5.10",
"@typescript-eslint/eslint-plugin": "^7.1.1",
"@typescript-eslint/parser": "^7.1.1",
"bufferutil": "^4.0.8",
"eslint": "^8.57.0",
"eslint-config-next": "^14.1.3",
"postcss": "^8.4.34",
"prettier": "^3.2.5",
"prettier-plugin-tailwindcss": "^0.5.11",
"prisma": "^5.13.0",
"tailwindcss": "^3.4.1",
"tsx": "^4.7.2",
"typescript": "^5.4.2"
},
rare-sapphire
rare-sapphire2y ago
I'm using a transaction so I believe I do need the serverless driver. I originally was using the JWT session strategy but I decided I wanted to swap to database sessions. I also thought maybe this issue was because of JWT sessions but it also happens this way.
I'm not clear on how this applies to the Neon driver. Sounds specific to your application.
[auth][cause]: 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: ws
does not work in the browser. Browser clients must use the native WebSocket object
[auth][cause]: 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: ws
does not work in the browser. Browser clients must use the native WebSocket object
This sounds to me like you're attempting to use the serverless driver in a client component. You need to use it in server-side code only.
ambitious-aqua
ambitious-aquaOP2y ago
I'm still newer to this so I could be doing it completely wrong. I'm creating my prisma client as seen in my first message. I'm currently not importing it at all in any client component. If I set nextauths session strategy to JWT I can login just fine. As soon as I attempt to use the db client in their session or jwt callback so that I can get updated user information into the session without them having to login again, I get this ws error. So I decided to try the database session instead and now on login I get this error every time which I assume is because nextauth is connecting to my database using my db client that is using the ws package.
rare-sapphire
rare-sapphire2y ago
@EXILE the error message states:
ws
does not work in the browser. Browser clients must use the native WebSocket object
ws
does not work in the browser. Browser clients must use the native WebSocket object
My guess is you're trying to run some database code in a client component (https://nextjs.org/learn/react-foundations/server-and-client-components) Find the code with 'use client' and start there.
React Foundations: Server and Client Components | Next.js
Learn about the server and client environments and when to use each.
rare-sapphire
rare-sapphire2y ago
The db code needs to run on the backend. It should never be run from the browser.
ambitious-aqua
ambitious-aquaOP2y ago
That's why I'm confused because I'm not using it on the client anywhere from what I can tell It only happens through nextauth I believe
rare-sapphire
rare-sapphire2y ago
Hmm, can you share the React component that calls auth? BTW, is the error printed in your terminal or browser?
ambitious-aqua
ambitious-aquaOP2y ago
terminal Okay so originally I was using JWT now I'm attempting to swap to database sessions right. So I'm getting this same websocket error but instead of only getting it if I attempted to access my database within the nextauth configs callbacks (jwt, session, whatever else) I'm not getting it on sign in. register works fine I can create an account, send a verification email, verify the account. but as soon as I try to login with that account or login with a provider I get this websocket error and it keeps me on the login page. if I go to the home page I see some sort of session as my user-menu displays the name + email (no image though), but if I attempt to go to the settings page it just redirects me back to the login page while continuing to throw that error. So I believe this error is happening way before even calling auth() to get session as it happens even on the login page on sign in attempt If I console log through the credentials sign in process it seems to access the db just fine but as soon as it gets to the signIn() function provided by nextauth is when the error happens. so with providers it does its thing fine but again as soon as it redirects from the providers successful login it shows the ws error and stays on the login page Every time I refresh a page I get the web socket error too so I believe thats due to middleware being ran since I'm not actually using the session on this page I log in my middleware and the error happens before all the logs so im not even sure I'm curious if you think this is an error from misuse, the neon driver or possibly due to version 5 of nextauth @ShinyPokemon Opened up a post in their disc just incase @ShinyPokemon Okay I got a different question here. Should my db client (that is using the serverless driver + websockets) be able to work in my middleware? Currently I don't use it but if I do try to use it in my middleware I get the same error
rare-sapphire
rare-sapphire2y ago
I'm not a Next.js expert, but I believe it should work just fine. Middleware is executed server-side as far as I know, but it's odd that you see the issue when adding the driver to middleware. If you can create a reproduction using just the Neon driver without NextAuth/Prisma then it sounds like something is up, but we'd need to rule out those libraries first IMO
ambitious-aqua
ambitious-aquaOP2y ago
When I call a query in my middleware I get the error. Same for in the JWT callback of nextauth. I could possibly try to make a minimal reproduction. As soon as I remove neonConfig.webSocketConstructor = ws; it works fine besides the issues removing this brings to the table I'll also note that if I try to query my database at all in either the jwt callback or in middleware almost every single request to any page on my website will take 15-20 seconds or timeout on vercel. Which I assume is from the websocket issue
ambitious-aqua
ambitious-aquaOP2y ago
@ShinyPokemon first time attempting to make a reproduction. I first tried the serverless driver as shown in the docs and it worked fine
No description
ambitious-aqua
ambitious-aquaOP2y ago
I then tried to use it with prisma
ambitious-aqua
ambitious-aquaOP2y ago
No description
ambitious-aqua
ambitious-aquaOP2y ago
and I get that same websocket error I can upload the repo in a sec I just want to try using the latest packages in this reproduction because I did end up downgrading to the same as my project Same issue with latest Except there is one more error with an open PR: https://github.com/prisma/prisma/pull/23754
⚠ ./node_modules/.prisma/client/wasm-edge-light-loader.js
The generated code contains 'async/await' because this module is using "topLevelAwait".
However, your target environment does not appear to support 'async/await'.
As a result, the code may not run as expected or may cause runtime errors.

Import trace for requested module:
./node_modules/.prisma/client/wasm-edge-light-loader.js
./node_modules/.prisma/client/wasm.js
./node_modules/.prisma/client/default.js
./node_modules/@prisma/client/default.js
./src/db.ts
⚠ ./node_modules/.prisma/client/wasm-edge-light-loader.js
The generated code contains 'async/await' because this module is using "topLevelAwait".
However, your target environment does not appear to support 'async/await'.
As a result, the code may not run as expected or may cause runtime errors.

Import trace for requested module:
./node_modules/.prisma/client/wasm-edge-light-loader.js
./node_modules/.prisma/client/wasm.js
./node_modules/.prisma/client/default.js
./node_modules/@prisma/client/default.js
./src/db.ts
ambitious-aqua
ambitious-aquaOP2y ago
GitHub
GitHub - LucasWinkler/finaltest
Contribute to LucasWinkler/finaltest development by creating an account on GitHub.
ambitious-aqua
ambitious-aquaOP2y ago
There's 2 branches. master should have the same package versions as my project while the other branch should have the latest
rare-sapphire
rare-sapphire2y ago
Hey, just to be clear. Do you mean this repo shows that using Prisma with Neon in middleware doesn't work?
ambitious-aqua
ambitious-aquaOP2y ago
Yes I'm sorry I didn't name it well at all I was just messing around It's showing that using prisma with the neon serverless driver over websockets does not work in next.js middleware
rare-sapphire
rare-sapphire2y ago
Got it. What commands do I need to run to test? Sorry if it's a dumb question, I just want to run it and test quickly. I assume some prisma migrate commands then npm run dev?
ambitious-aqua
ambitious-aquaOP2y ago
- .env: DATABASE_URL with the -pooler flag - npx prisma generate or npx prisma migrate will work I believe? - npm run dev I think thats all that was needed
rare-sapphire
rare-sapphire2y ago
@EXILE thanks, BTW, reset your database password immediately. You appear to have pushed it in .env in that repo
ambitious-aqua
ambitious-aquaOP2y ago
ye I see that tyty Forgot it doesn't create a gitignore by default with it lmao
rare-sapphire
rare-sapphire2y ago
That's crazy. For real?
ambitious-aqua
ambitious-aquaOP2y ago
# local env files
.env*.local
# local env files
.env*.local
I lied it does I remember now that they suggest using .env.local but I prefer using .env
rare-sapphire
rare-sapphire2y ago
OK, something weird is going on It's not just middleware. Even trying to query from the db.ts on initial load fails The ws library has a browser.js file and for some reason this is being loaded
ambitious-aqua
ambitious-aquaOP2y ago
It's weird because I'm able to query/mutate in server actions and in certain callbacks from nextauth as well. Not sure why it doesn't work in those cases.
rare-sapphire
rare-sapphire2y ago
Yeah, this is odd. So I see that removing neonConfig.webSocketConstructor = ws works Is that a viable option?
ambitious-aqua
ambitious-aquaOP2y ago
Correct me if I'm wrong but I believe in order to use transactions I need to use the ws package. I also noticed that yes I can remove that line to prevent the error but then in those same spots where the error would occur, I now get super slow requests in prod So if my middleware runs and theres a db query or in my nextauth callback etc.. my request to that page takes like 20 seconds and times out
rare-sapphire
rare-sapphire2y ago
Gotcha. Damn, this is weird. Can you maybe ask in Next.js and Prism Discord servers?
ambitious-aqua
ambitious-aquaOP2y ago
Yeah I'll post it around as soon as I can
rare-sapphire
rare-sapphire2y ago
AFAICT, the ws library is loading the browser file (https://github.com/websockets/ws/blob/master/package.json#L24) and this immediately fails to connect (by design)
GitHub
ws/package.json at master · websockets/ws
Simple to use, blazing fast and thoroughly tested WebSocket client and server for Node.js - websockets/ws
rare-sapphire
rare-sapphire2y ago
My guess is that it's the Next.js bundler that is loading that browser shim instead of the Node.js one. @Mahmoud have you seen this before? @EXILE downgrade to next@14.1 It works. I don't know why yet Sorry, ignore that...it's a different error
rare-sapphire
rare-sapphire2y ago
GitHub
nextjs-neon-prisma/package.json at main · evanshortiss/nextjs-neon-...
Contribute to evanshortiss/nextjs-neon-prisma development by creating an account on GitHub.
rare-sapphire
rare-sapphire2y ago
Try this project. It's working for me
ambitious-aqua
ambitious-aquaOP2y ago
All good. I did notice I do have that topLevelAwait error here but not in my project. Also during next build in my project I do get some other prisma related errors I believe. They seemed to be around logging and only happen during build but the app works fine besides my current issues. I just assumed those warnings/errors didn't matter much so I haven't looked into it. Probably will after solving the main issue here Thanks I'll take a look in a bit. Maybe 25 mins or so @ShinyPokemon Alright so I'm testing your project and it works for me. I'm curious what resolved it for you. Was it specific versions or was it mainly the pool you setup. Also just curious if I'm still able to instantiate the prisma client the same way I was doing before (similar to: https://www.prisma.io/docs/orm/more/help-and-troubleshooting/help-articles/nextjs-prisma-client-dev-practices). I should probably just try it instead of asking and I will but thought I would ask. Oh just noticed the comment in the seed.ts as well I'm also curious about the (pool as any). What makes this one different than the other events and is there a correct way to type this if it needs to be called like this Trying this out in my project and I still get that same websocket error hmm. maybe im missing something @ShinyPokemon Alright another update here. I went back to your project and created a middleware.ts file that fetches users and again that same websocket issue. Your project works for the actual page fetching but not middleware and I tried to apply what you've done to my actual project and once again the same issues happen in nextauth callbacks and middleware
ambitious-aqua
ambitious-aquaOP2y ago
GitHub
Preview feature feedback: @prisma/adapter-neon / Neon Serverless ...
Support for the Neon Serverless Driver is in preview behind the driverAdapters preview feature. Please share your feedback about @prisma/adapter-neon released in v5.4.0 in this issue. If you encoun...
ambitious-aqua
ambitious-aquaOP2y ago
Going to experiment with this but I'm pretty sure the native WebSocket is supposed to be used in client only?
ambitious-aqua
ambitious-aquaOP2y ago
Okay well I tried that and it got rid of local errors. In dev I get vercel edge function timed out. I tried swapping between ws and WebSocket based on node env and still same issue. Tried to see if it was an issue with something else in my project but it is an issue with the current ws error. If I remove the websocket constructor my application is extremely slow and times out but if I keep it I get errors and things just dont work properly.
No description
harsh-harlequin
harsh-harlequin2y ago
Not really I'm trying to catch up on the thread, but to make sure, the issue only happens when using the Neon Serverless Driver, right?
ambitious-aqua
ambitious-aquaOP2y ago
Yes, when I use the neon serverless driver over websockets (ws package) I am unable to call my database using prisma in middleware or nextauths jwt callback. I'm also unable to use the database strategy at all with nextauth as it calls my db itself obviously for session management. I'm using JWT anyways but I did attempt the database strategy at one point because I thought maybe it would solve something. Without the websocket constructor locally things seem alright but as soon as I push to prod I get vercel edge function timed out on almost every single dynamic request and the app is very slow Now I don't need to call my database in my middleware and I probably wont I think? but I was just using it to test Again the error does not happen in server components/actions or while seeding I appreciate all the help so far
harsh-harlequin
harsh-harlequin2y ago
Yes, when I use the neon serverless driver over websockets (ws package
Have you tried remove the ws package dependency? AFAIK it's needed for older Prisma versions So no need for this part
import ws from 'ws';
neonConfig.webSocketConstructor = ws;
import ws from 'ws';
neonConfig.webSocketConstructor = ws;
ambitious-aqua
ambitious-aquaOP2y ago
I've tried removing that webSocketConstructor and I do not get any errors anymore but then when I push to prod my edge functions timeout and pages don't load anymore. they are typically very fast
ambitious-aqua
ambitious-aquaOP2y ago
I'm using only 1 transaction atm in my app (not 100% if I need it but im trying it out)
No description
ambitious-aqua
ambitious-aquaOP2y ago
and I thought I needed websockets due to this I'll try removing it once more though
ambitious-aqua
ambitious-aquaOP2y ago
- Local dev: works fast and normal with no errors at all - Local build: Same as local dev very fast - Prod: dynamic routes are very slow and typically timeout Image 1: Local build + prod both get the warnings as shown in the terminal but these do also happen regardless of if I'm using websockets or not. I dont know much but reading this it looks ok? just seems like certain debugging functions use node.js apis Image 2: Just my static/dynamic pages. Image 3: Showing my requests timing out. You can see /courses times out but I'm doing literally nothing on that page rn. The size of it is small too I believe. If I were to bring back the websocket constructor and stop calling my database in certain areas of my app then these requests would not time out. They are normally pretty fast.
No description
No description
No description
ambitious-aqua
ambitious-aquaOP2y ago
@Mahmoud The struggles of trying to make some personal projects so I can get my first job lmao. I just want to get some projects out I've been slacking
rare-sapphire
rare-sapphire2y ago
@EXILE this issue is happening to me here too: https://github.com/vercel-labs/function-database-latency/pull/49
GitHub
Add Neon with Drizzle, Prisma, and Node.js support by evanshortiss ...
@jawj @kelvich can you take a look? Two outstanding questions: Is there a seed script required? I don't see prisma migrate being called anywhere for other platforms, so maybe not? Prisma runs ...
rare-sapphire
rare-sapphire2y ago
It's only Prisma in edge mode. It seems fine otherwise. Still not sure why
ambitious-aqua
ambitious-aquaOP2y ago
Interesting. I'll keep watching this PR and I guess I'll stop using my database in these certain areas for now and work on the other areas of the project that don't require that. I appreciate everything. I thought it could've been misuse for sure I swear every time I make a project I run into something that gets in the way like this. Never a smooth experience Just curious what the next steps would be for this? @ShinyPokemon I don't know if I continue using the webSocketConstructor but I just dont query in the areas that run on the edge or if I try some other method to create my db client
rare-sapphire
rare-sapphire2y ago
@EXILE I'm not sure right now. I can confirm I see a similar issue with Prisma on edge functions. Random gateway timeouts I'll need to investigate further Will keep you posted
ambitious-aqua
ambitious-aquaOP2y ago
Sounds good ty
rare-sapphire
rare-sapphire2y ago
Sorry I don't have more to go on yet. Very frustrating, I know
ambitious-aqua
ambitious-aquaOP2y ago
It's all good I can work around it for now I appreciate it a lot
rare-sapphire
rare-sapphire2y ago
@EXILE are you using AppRouter or Pages in Next.js?
ambitious-aqua
ambitious-aquaOP2y ago
App router Decided I would try it out this time around
ambitious-aqua
ambitious-aquaOP2y ago
Sure and you want me to try this with the websocket constructor removed correct?
rare-sapphire
rare-sapphire2y ago
Correct My theory is, when run on the edge, the Pool sockets stay open. Setting the idle timeout forces them to close, and stops the prior inovation getting "stuck" open It's odd though. I thought multiple instances of the function should be able to run just fine, so it shouldn't matter than the socket in one is stuck open
ambitious-aqua
ambitious-aquaOP2y ago
Oh also why were you calling pool.end instead of the prisma client disconnect while seeding? I saw you commented out the prisma version I do notice that when I call db.disconnect it seems to hang or take a while to end the seed script but with pool.end its instant @ShinyPokemon With idleTimeoutMillis: 1 my project does seem to perform at proper speeds now
rare-sapphire
rare-sapphire2y ago
I am confused, but also satisfied we have a theory and workaround What’s really bothering me is that it’s not consistent. My other repo that I linked above doesnt have this issue at all
ambitious-aqua
ambitious-aquaOP2y ago
I wish I could give some type of input but I'm very inexperienced
rare-sapphire
rare-sapphire2y ago
You’ve been super helpful in testing and debugging this @EXILE small update Use ctx.waitUntil as shown here, instead of idleTimeoutMillis. I didn't know Vercel edge supported this: https://github.com/vercel-labs/function-database-latency/pull/50/files
ambitious-aqua
ambitious-aquaOP2y ago
Sorry where exactly do I use this? I have my prisma client singleton that I import into my data-access functions. Would I need to use it in each function, somewhere when creating the prisma client, or elsewhere. I'm not too sure. I see you're wrapping the pool.end with it.
No description
No description
rare-sapphire
rare-sapphire2y ago
I think you'd add it in the createPrismaClient function Actually, scratch that...let me think. I guess you'd need to add it to the end of your API routes So the pool needs to be exported There's probably a better pattern though, that's less tedious. Maybe something like:
export async function runFnWithClient (fn: (db: PrismaClient) => Promise<void>) {
// perform pool, adapter etc initialisation here

try {
// run the function, passing the client as an argument
await fn(client)
} finally {
waitUntil(pool.end())
}
}
export async function runFnWithClient (fn: (db: PrismaClient) => Promise<void>) {
// perform pool, adapter etc initialisation here

try {
// run the function, passing the client as an argument
await fn(client)
} finally {
waitUntil(pool.end())
}
}
Then in your API endpoints:
export default async function api(req: Request) {
return runFnWithClient(async (client) => {
await client.user.delete({ where: { id } })

return Response.json({ message: 'deleted' })
})
}
export default async function api(req: Request) {
return runFnWithClient(async (client) => {
await client.user.delete({ where: { id } })

return Response.json({ message: 'deleted' })
})
}
Like I said, maybe there's a better pattern, but this is just a thought.
harsh-harlequin
harsh-harlequin2y ago
Yea waitUntil should be at the end (I assume this is similar to Cloudflare Workers)
rare-sapphire
rare-sapphire2y ago
Yep. Also, I think we need to better promote PrismaNeonHTTP since it would avoid this and provide better performance, albeit without full transaction support
ambitious-aqua
ambitious-aquaOP2y ago
Is there any downside to me just using idleTimeoutMillis: 1 solution or is it fine for me to use. It's been working so I've sort of just been going with that
rare-sapphire
rare-sapphire2y ago
Evan Shortiss 🇮🇪 🇺🇸 (@evanshortiss) on X
Switching from a WebSocket-based Postgres Pool to SQL over HTTP yields impressive results in queries from edge functions. An article explaining this is in the next tweet 👇
From An unknown user
Twitter
ambitious-aqua
ambitious-aquaOP2y ago
Sounds good

Did you find this page helpful?