DEV Community

kyohei
kyohei

Posted on

Integrating Svelte Kit and Prisma without wrapper class to workaround

Svelte Kit and Prisma is a go-to stack for me these days. I like its great ergonomics in frontend and less cost of cognitive load from context switches between server sides.

Users of Svelte Kit, Nuxt or other Vite based frameworks haven't been able to import PrismaClient or other exports in the way the documentation suggests1.

doc says...

which leads you to see:


import Prisma, { PrismaClient } from "@prisma/client";
                 ^^^^^^^^^^^^
SyntaxError: Named export 'PrismaClient' not found. The requested module '@prisma/client' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:

import pkg from '@prisma/client';
const { PrismaClient } = pkg;
Enter fullscreen mode Exit fullscreen mode

While there's several workarounds known in the community, they still have some shortcomings like inability to import Enum values or Prisma object for SQL templates nicely.

For the context, when you use Prisma, what you use is not something in @prisma/client, but a code locally generated prisma generate command, which in turn @prisma/client would import inside. Since Prisma need to provide you a custom made API that reflects your schema, it wouldn't able to come as like an ordinary package.

As a result, @prisma/client contains just a line of code or two, to import the custom-made client supposedly generated at the time of deployment. It's really small piece of code:

const prisma = require('.prisma/client/index')

module.exports = prisma
Enter fullscreen mode Exit fullscreen mode

Since this glues the application code and generated code, I would call this as Prisma's glue code.

This works fine for CJS code to require. Similarly, the code that would be transpiled by TypeScript or other bundlers can work with this, since those tools take care at compile time.

The problem is ES modules, who cannot recognize any exports other than default exports.

Svelte Kit (as far as I know also Nuxt or other Vite based frameworks, depending on configs) emits ES modules as their build artifacts, unlike Next that emits CJS bundles, and encounters this problem.

But why?

When an ES module tries to import named exports from other CJS modules, Node recognizes only the exports that explicitly defiend in specific syntax. They would be detected before they get executed with static analysis 2

For better compatibility with existing usage in the JS ecosystem, Node.js in addition attempts to determine the CommonJS named exports of every imported CommonJS module to provide them as separate ES module exports using a static analysis process.

The detection of named exports is based on common syntax patterns but does not always correctly detect named exports.

Because of this, Prisma's glue code effectively reexports just default export from perspective of Node, while TS compiler somehow recognized the named exports. This causes a weird situation where they would work fine in dev server, but fails with the error message like this once they have been bundled for production, as ES modules with static import s.

Fortunately, Node doesn't detect only export.name = value. It seems to support more involved syntax, like 3:

module.exports = {
  ...require('.prisma/client/index'),
}
Enter fullscreen mode Exit fullscreen mode

Which I sent as a pull request to Prisma. I'm not sure when or even if it would be accepted, I assume you would be able to use this hook in package.json for the time being. Assuming above glue code replacement (JS snippet) saved as patches/prisma-fixed-glue.js, add this:

  "build": "cp patches/prisma-fixed-glue.js node_modules/@prisma/client/index.js && svelte-kit build",
Enter fullscreen mode Exit fullscreen mode

(I use pnpm which no longer supports lifecycle scripts in default, but you can put prebuild as long as you enabled it or use npm)


  1. https://www.prisma.io/docs/concepts/components/prisma-client/working-with-prismaclient/instantiate-prisma-client 

  2. https://nodejs.org/api/esm.html#commonjs-namespaces 

  3. https://github.com/nodejs/cjs-module-lexer/tree/1.2.2 

Top comments (2)

Collapse
 
gf_developer profile image
GF

It's also possible to achieve a goal with the following code:

import * as prismaPkg from '@prisma/client'
import pkg from '@prisma/client'

const prismaConstructor = prismaPkg?.PrismaClient ?? pkg.PrismaClient
export default new prismaConstructor()
Enter fullscreen mode Exit fullscreen mode
Collapse
 
tnzk profile image
kyohei

Well that's no longer necessary because my PR is merged in Prisma 3.14.