prisma: Can't use Prisma client in Next.js middleware, even when deploying to Node.js

Bug description

Next.js middleware seems to fail the edge runtime check, even when deployed to Node.js:

Error: PrismaClient is unable to run in Vercel Edge Functions. As an alternative, try Accelerate: https://pris.ly/d/accelerate.
If this is unexpected, please open an issue: https://github.com/prisma/prisma/issues

Wasn’t sure if this is a Next or a Prisma bug.

How to reproduce

  1. Clone https://github.com/markspolakovs/next-prisma-repro
  2. Run yarn dev
  3. Visit http://localhost:3000 image

Expected behavior

DB operations to work inside middleware.

Prisma information

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}

model User {
  id Int @id @default(autoincrement())
}
import { PrismaClient } from "@prisma/client";
import { NextRequest, NextResponse } from "next/server";

const prisma = new PrismaClient();

export async function middleware(req: NextRequest): Promise<NextResponse> {
  const user = await prisma.user.findFirst();
  return NextResponse.next();
}

Environment & setup

  • OS: macOS arm64
  • Database: SQLite
  • Node.js version: v18.17.0

Prisma Version

prisma                  : 5.3.1
@prisma/client          : 5.3.1
Current platform        : darwin-arm64
Query Engine (Node-API) : libquery-engine 61e140623197a131c2a6189271ffee05a7aa9a59 (at node_modules/@prisma/engines/libquery_engine-darwin-arm64.dylib.node)
Schema Engine           : schema-engine-cli 61e140623197a131c2a6189271ffee05a7aa9a59 (at node_modules/@prisma/engines/schema-engine-darwin-arm64)
Schema Wasm             : @prisma/prisma-schema-wasm 5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59
Default Engines Hash    : 61e140623197a131c2a6189271ffee05a7aa9a59
Studio                  : 0.494.0

About this issue

  • Original URL
  • State: open
  • Created 9 months ago
  • Reactions: 14
  • Comments: 31 (10 by maintainers)

Most upvoted comments

A solution for anyone else who ran into this error just trying to set up a project - I believe you can just use jwt sessions, rather than database sessions.

export const config = {
  adapter: PrismaAdapter(prisma),
  session: { strategy: "jwt" },      // Add this line
  callbacks: ...
  providers: ...,
} satisfies NextAuthConfig;

Fortunately, we just released Prisma ORM version 5.11.0 which includes a preview feature for Edge Functions support for Vercel Edge Functions and Middleware (and Cloudflare Workers and Pages) in Prisma ORM 🥳

Please give it a try, and let us know how it goes! If you encounter any problems, please create a new bug report issue, or if the problem is driver adapter specific, use the feedback discussions for @prisma/adapter-neon, @prisma/adapter-planetscale, or @prisma/adapter-libsql / Turso 🙇


With that I was able to upgrade the reproduction from the original issue description to Prisma 5.11.0, and use a PostgreSQL database from Neon (or Vercel Postgres): https://github.com/markspolakovs/next-prisma-repro/pull/1 (Unfortunately the Edge Runtime does not support reading local files, so using SQLite - which is a local file - is out of the question, so I had to switch to using PostgreSQL.)

This is very annoying, I dont use Neon or Planetscale, or even Vercel or Cloudflare edge functions.

I just want to roll my own middleware and my own database on my own server. It makes no sense why I cant just invoke the client locally. Why do we have to use a special Neon adapter or have a cloudflare account or use Accelerate? Why cant we just invoke local stuff in our middleware? This makes no sense. Overengineered garbanzo.

@janpio Might be true, but that is not a relevant fact in my case since I don’t use Vercel. I deploy Next.js apps as Docker containers that run in Kubernetes. The base Docker image is always node:21-alpine.

Why would querying the DB be any different in middleware than in a server component? To the DB client, it’s all just a Node.js runtime.

I hope I’m wrong, but this seems like a deliberate limitation to sell some commercial feature. Which is really outside the spirit and ethos of open source (see OSI).

 ⨯ PrismaClient is unable to run in Vercel Edge Functions or Edge Middleware. As an alternative, try Accelerate: https://pris.ly/d/accelerate.

Pretty annoying. Moving the very same function call out of the middleware and it works. This was my workaround for now, but I would have preferred to keep the code in the middleware where it was before we started using Primsa. 🙈

@defrex

Respectfully you are mistaken. This problem was created from the decision to redefine middleware as “edge middleware” which is a complete deviation of what “middleware” means in the context of a web framework. Vercel has decided to lock a basic feature of web frameworks (middleware that is executed before/after a request) behind a vendor-specific implementation. It’s like a scope creep that omits the original feature.

If there was “edge middleware” and regular middleware, than this entire thread would not exist. If they had released only “Edge Middleware” than this Github Issue would be a Feature Request to “Add normal middleware” instead of a bug because we wouldn’t have a false expectation. I’m using the term “middleware” as its conventionally used in Nest.js, Django, Laravel, Rails and other frameworks.

I’m happy to report this strategy worked! I don’t know about dusting off the blog, but here is a quick summary for future travellers.

Local Neon-Enumating Websocket Proxy

Add the @neondatabase/wsproxy to your docker-compose.yaml.

In this example db is the name of my postgres container, swap it out for your own!

  neon_wsproxy:
    container_name: neon-wsproxy
    image: ghcr.io/neondatabase/wsproxy:latest
    environment:
      APPEND_PORT: 'db:5432'
      ALLOW_ADDR_REGEX: '.*'
      LOG_TRAFFIC: 'true'
      LOG_CONN_INFO: 'true'
    ports:
      - '5488:80'
    depends_on:
      - db
    restart: unless-stopped
    healthcheck:
      test: ['CMD', 'nc', '-z', '127.0.0.1', '80']
      interval: 5s
      timeout: 2s
      retries: 20

Then, initialize your PrismaClient and PrismaNeon adapter with a few extra touches.

// Polyfill for WebSocket in Node.js
if (global.WebSocket === undefined) { // don't break Edge, where `global.WebSocket` pre-exists
  const ws = require('ws')
  neonConfig.webSocketConstructor = ws
}

let databaseUrl = process.env.DATABASE_URL

// Configure the Neon adapter to connect to the local Neon proxy
if (databaseUrl.includes('localhost')) {
  // we edit the direct `DATABASE_URL` here, so `.env` can still be used by the `prisma` CLI
  databaseUrl = editDatabaseUrl(databaseUrl, { port: 5488 })

  neonConfig.wsProxy = () => `127.0.0.1:5488/v1`
  neonConfig.useSecureWebSocket = false
  neonConfig.pipelineConnect = false
}

const pool = new Pool({ connectionString: databaseUrl })
const adapter = new PrismaNeon(pool)
const prisma = new PrismaClient({
  adapter,
  datasources: {
    db: { url: databaseUrl },
  },
})

Et voila! This prisma instance shout be GTG for use in the Edge runtime, with either a local or Neon-hosted postgres instance.

@petercunha @iMerica I can’t speak to the business motivations here (I don’t work for Vercel or Prisma), but there is an actual technical issues at play.

Middleware runs on an Edge runtime, same as Cloudflare Workers. I suspect this choice was made for performance reasons. Ie if you’re going to run a separate process before every request, you’d better make it a fast one. But by separating the process out, you can serve static or cached assets to the client, without needing to make a full server-render run. Otherwise Next.js with auth would have similar perf characteristics to Rails or Django.

However, the Edge runtime only has access to web-standard APIs, without any Node.js extensions. This means it can’t use unix sockets or tcp to connect to the database. That leaves HTTP or WebSockets. The request/response overhead makes using HTTP prohibitive.

Neon and Planetscale both support WebSocket connections out of the box, which is why they can be supported. The code above shows how you can use a proxy service to allow a WebSocket connection to a regular Postgres instance, this could easily be used in production if you’re willing & able to run that container.

@petercunha

As far as I can tell this issue was a product decision made by Vercel. It seems like they have completely lost the plot – forgetting that they are the stewards of an open source project web framework (Next.js) - not a Vendor web framework called Vercel.js. It’s not over-engineered, it’s wrongfully engineered.

If it was “open” this middleware would be interoperable in any cloud/server environment instead of exclusively on Vercel. Sadly, Next.js is becoming fauxpen source.

@janpio Thanks for the update, instructions, and the example PR!

I upgraded prisma in my project and was also able to use prisma via the neon adapater in my middleware function…slick!

A solution for anyone else who ran into this error just trying to set up a project - I believe you can just use jwt sessions, rather than database sessions.

export const config = {
  adapter: PrismaAdapter(prisma),
  session: { strategy: "jwt" },      // Add this line
  callbacks: ...
  providers: ...,
} satisfies NextAuthConfig;

i can confirm that adding session: { strategy: "jwt" } is working for me, i’m not sure why people gave thumbs down

Unfortunately it looks like Next’s edge runtime blocks all this trickery: I tried the client/index import that @millsp suggested, and also set unstable_allowDynamic in my middleware config, but Prisma Client still fails to load:

 ⨯ Error [TypeError]: wr1.deprecate is not a function
    at <unknown> (webpack-internal:///(middleware)/../utility/prisma/client/runtime/library.js:388)
    at eval (webpack-internal:///(middleware)/../utility/prisma/client/runtime/library.js:388:22)
    at eval (webpack-internal:///(middleware)/../utility/prisma/client/runtime/library.js:16:30)
    at eval (webpack-internal:///(middleware)/../utility/prisma/client/runtime/library.js:524:128)
    at eval (webpack-internal:///(middleware)/../utility/prisma/client/runtime/library.js:16:30)
    at eval (webpack-internal:///(middleware)/../utility/prisma/client/runtime/library.js:1064:12)
    at (middleware)/../utility/prisma/client/runtime/library.js (file:///Users/marks/code/bowser/server/.next/server/middleware.js:1117:1)
    at __webpack_require__ (file:///Users/marks/code/bowser/server/.next/server/edge-runtime-webpack.js:37:33)
    at fn (file:///Users/marks/code/bowser/server/.next/server/edge-runtime-webpack.js:325:21)
    at eval (webpack-internal:///(middleware)/../utility/prisma/client/index.js:6:324)
    at (middleware)/../utility/prisma/client/index.js (file:///Users/marks/code/bowser/server/.next/server/middleware.js:1095:1)

(I ran runtime/library.js through prettier to help with debugging, hence the line numbers being different.)

It looks like it’s getting tripped up on Prisma requiring debug which then requires Node’s util module to call util.deprecate - unfortunately I’d imagine that trying to solve this will just lead to a game of whack-a-mole with various Node modules the Edge Runtime doesn’t support.

I found a feature request on the Next side to allow configuring middleware to use the Node runtime, but with no response from the Next team: https://github.com/vercel/next.js/discussions/38989

@iMerica you are correct. I thought this was Prisma’s fault at first, but it turns out that edge middleware is a vendor lock in strategy by Vercel.

Unfortunately the only way around this is to build a proxy server in front of Next.js that handles middleware.

There is a thread about this on Next’s repo that has been open for over a year with no updates: https://github.com/vercel/next.js/discussions/46722

Looks like Vercel has no intention of allowing self hosted middleware any time soon.

The Next.js documentation has the answer to the problem:

image

Runtime

Middleware currently only supports the Edge runtime. The Node.js runtime can not be used.

Source https://nextjs.org/docs/app/building-your-application/routing/middleware#runtime

So even when you run your Next.js app locally via Node.js, it unfortunately still runs the middleware via the Edge middleware instead.

Then you should open a bug report instead of assuming ill intent, so we can look into that, because that of course should not happen. Obviously the error message is incorrect, and we want to look into that and fix it. (I am not fully sure if that is also the case above, so an additional bug issue with information and optimally a reproduction would be very helpful.)

PS: We are also currently working on, and have a private Early Access running, for support of Vercel Edge Middleware: https://github.com/prisma/prisma/issues/21394