Open juhawilppu opened 11 months ago
Hi @juhawilppu. Typescript types should be stripped before anything gets to the browser - not just for Prisma + Next.js but pretty much for any TS project. Could you provide a small reproduction where we could see it happening?
Yes, I made a small code example here https://github.com/juhawilppu/prisma-nextjs-db-schema-problem.
Just run
npm i
npm run dev
and then open browser to http://localhost:3000/dynamic
.
You will see this
The problem happens when forcing rendering to client-side
# app/dynamic/page.tsx
const DynamicActions = dynamic(() => import('./DynamicActions'), {
ssr: false,
})
and then importing a prisma enum in the client-side
# app/dynamic/DynamicActions.tsx
import { UserStatus } from '@prisma/client'
const test = () => {
if ('ACTIVE' == UserStatus.ACTIVE.toString()) {
return <div>The above code line causes the database schema to show in the chunk</div>
}
return <div>Test</div>
}
export default test
I now see that this can easily be avoided by not importing the UserStatus
type in client-side. However, I didn't expect doing this would show the whole database schema, including PrivateTable
, in client-side.
you should probably use import type instead of import
It might also be a good idea to enable this eslint rule
Just curious, your backend for fronted is directly manipulating the data store via Prisma? You don't have an intermediary API be REST/GraphQL, etc. that abstracts (and protects) these operations?
you should probably use import type instead of import
Yeah, true. But using import type
makes it not possible to use the type like a regular enum. I ended up making a TS duplicate of the enum and only use that in the code.
Just curious, your backend for fronted is directly manipulating the data store via Prisma? You don't have an intermediary API be REST/GraphQL, etc. that abstracts (and protects) these operations?
The frontend does not have a connection to the database. In the original code I'm rendering content based on the user's status which is obtained from the session - database is not even used in that view.
NextJS is, however, used for server-side rendering. So there are views which do make queries to the database because they're ran in the backend. Those views can then use the so-called hydration process to switch the rendering to the client-side and render dynamic content and make REST calls to endpoints using prisma. The line between backend and frontend is quite blurred and you need to be careful with it.
Just curious, your backend for fronted is directly manipulating the data store via Prisma? You don't have an intermediary API be REST/GraphQL, etc. that abstracts (and protects) these operations?
Just curious, what do you mean by intermediary? isn't nextJS's api routes enough to connect to the db via prisma to abstract and protect those routes?
Just curious, your backend for fronted is directly manipulating the data store via Prisma? You don't have an intermediary API be REST/GraphQL, etc. that abstracts (and protects) these operations?
Just curious, what do you mean by intermediary? isn't nextJS's api routes enough to connect to the db via prisma to abstract and protect those routes?
The path to software maintenance hell is paved by logic misplaced in the frontend tier, to say nothing of the audit and compliance related risks in heavily regulated areas like healthcare and finserv.
Granted, depending on what you are building there may be a time and place for it. But it seems every 10-15 years, and particularly these days within the Javascript/Typescript ecosystem, we insist on refighting battles over conceptual ground already littered with bodies from other stacks.
and then open browser to
http://localhost:3000/dynamic
.
@juhawilppu there is no dynamic route in the repo above, I tried to reproduce adding your snippets, but no success, even the schema is different w/o enum, did you face the same issue for production build? you can try running start & build locally to emulate, curious about this
Problem
When using NextJS, all the autogenerated prisma model TypeScript types are visible to the client-side. Effectively this exposes the whole database schema to client-side. There are many tables and fields that should not visible to the clients, like log tables. In general, the backend implementation and how the data is stored, should not be visible to clients.
No data gets leaked, only the schema.
This relates to fields such as
- encryptedSocialSecurityNumber
- encryptedPhoneNumber
- encryptedEmail
Suggested solution
Add a
@@visibility
setting to models and fields. Don't generate client-side types when visibility is set tobackend
.This solution is similar to #5042 except it applies to the type definitions.
model PatientInformation { id Int @id @default(autoincrement()) encryptedSocialSecurityNumber String encryptedPhoneNumber String encryptedEmail String @@map("patient_information") @@visibility("backend") }
Additional context
Screenshot of the problem:
I can confirm that this is happening to me too.
My current WIP approach is to remove all the Prisma client imports from client components, and validate that the Prisma schemas are not exposed as part of the client bundled code.
I'm using Prisma in server actions, on my mind, that code should not be part of the client/frontend bundled.
you should probably use import type instead of import
How about when we need to import Enum?
you should probably use import type instead of import
How about when we need to import Enum?
you could import a union instead import type {$Enums} from @prisma/client $Enums.YourEnum as string | string | string
you should probably use import type instead of import
How about when we need to import Enum?
I ended up creating a new Enum 😔
You could try the ferka123's approach, I think if you do an import of prisma client (even for making an union), the schema is going to be exposed anyway 🤔
Same issue use next.js server action, prisma schema got leak on bundle.
Same issue use next.js server action, prisma schema got leak on bundle.
Using a server action? do you have a code example?
I'm using server actions too and don't have the prisma schema leaked on the bundle.
In theory, a server action is like a lambda or a serverless function that the frontend communicates via HTTP requests, so it should not be part of the bundled code in the browser 🤔
Same issue use next.js server action, prisma schema got leak on bundle.
Using a server action? do you have a code example?
I'm using server actions too and don't have the prisma schema leaked on the bundle.
In theory, a server action is like a lambda or a serverless function that the frontend communicates via HTTP requests, so it should not be part of the bundled code in the browser 🤔
use on lib next-safe-action (https://next-safe-action.dev/).
you should probably use import type instead of import
How about when we need to import Enum?
you could import a union instead import type {$Enums} from @prisma/client $Enums.YourEnum as string | string | string
Yes, I got it, we can use $Enums directly in server side. But when used in client side, import type of $Enums instead. It seems we can import type always without caring about the import difference.
Problem
When using NextJS, all the autogenerated prisma model TypeScript types are visible to the client-side. Effectively this exposes the whole database schema to client-side. There are many tables and fields that should not visible to the clients, like log tables. In general, the backend implementation and how the data is stored, should not be visible to clients.
No data gets leaked, only the schema.
This relates to fields such as
Suggested solution
Add a
@@visibility
setting to models and fields. Don't generate client-side types when visibility is set tobackend
.This solution is similar to https://github.com/prisma/prisma/issues/5042 except it applies to the type definitions.
Additional context
Screenshot of the problem: