Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Prisma to v5 #2231

Merged
merged 19 commits into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions waspc/cli/exe/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -143,11 +143,7 @@ setDefaultCliEnvVars = do
where
cliEnvVars :: [(String, String)]
cliEnvVars =
[ ("PRISMA_HIDE_UPDATE_MESSAGE", "true"),
-- NOTE: We were getting errors from Prisma v4 related to their Checkpoint system
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok sweet!

-- (which checks for updates that we don't want anyway), so now by default
-- we turn it off. Once we switch to Prisma v5, try removing this.
("CHECKPOINT_DISABLE", "1")
[ ("PRISMA_HIDE_UPDATE_MESSAGE", "true")
]

{- ORMOLU_DISABLE -}
Expand Down
4 changes: 2 additions & 2 deletions waspc/data/Cli/templates/basic/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
"typescript": "^5.1.0",
"vite": "^4.3.9",
"@types/react": "^18.0.37",
"prisma": "4.16.2"
"prisma": "5.18.0"
}
}
}
7 changes: 2 additions & 5 deletions waspc/data/Generator/templates/sdk/wasp/auth/lucia.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,8 @@ import { prisma } from 'wasp/server'
import { type {= userEntityUpper =} } from "wasp/entities"

const prismaAdapter = new PrismaAdapter(
// Using `as any` here since Lucia's model types are not compatible with Prisma 4
// model types. This is a temporary workaround until we migrate to Prisma 5.
// This **works** in runtime, but Typescript complains about it.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Always love seeing these kind of changes!

prisma.{= sessionEntityLower =} as any,
prisma.{= authEntityLower =} as any
prisma.{= sessionEntityLower =},
prisma.{= authEntityLower =},
);

// PRIVATE API
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Without this import, Prisma types are resolved incorrectly.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sodic this is the fix I implemented in the end, it slightly nudges the FieldRef type to work with our SuperJSON serialization.

I went down the rabbit hole of recursively converting all interfaces into types using Pick<T, keyof T> (as suggested by some in the Github issue) but this ended up being less problematic and simple enough.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code comment doesn't fully explain what's happening and what the tradeoffs are. Do you mind adding this playground link too.

It currently implies that type types have an implicit index signature (which isn't really true, they only exhibit special subtyping behavior). The comment also doesn't mention the compromise we made by declaring an extension to the FieldRef interface.

Anyway, both things should be clear from the playground, and you can adjust it as you see fit.

BTW, what happend with trying to fix the root of the problem, the definition of the JSONSerializable type (as attempted here). Did Superjson make it too complex?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated the comment to mention the playground to make it more complete as an explanation.

BTW, what happend with trying to fix the root of the problem, the definition of the JSONSerializable type (as attempted here). Did Superjson make it too complex?

We concluded that JSONSerializable would stop users from using the Prisma field reference feature: https://www.prisma.io/docs/orm/reference/prisma-client-reference#compare-columns-in-the-same-table since the proposed approach drops fields.

Then we set on to recursively convert all interfaces to types - which in the end was a long way of doing this d.ts patch we have in this repo.

import * as runtime from '@prisma/client/runtime';

// Prisma generated types which we use as default input and output types for CRUD
// operations internally use interfaces for some types.
// Our SuperJSON serialization types throw a type error when used with interfaces
// because interfaces don't have a index signature.
// We augment the Prisma generated types to have an index signature.
// Read more https://github.com/microsoft/TypeScript/issues/15300#issuecomment-1320528385
declare module '@prisma/client/runtime/library.js' {
export interface FieldRef<Model, FieldType> {
[key: string]: any;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import type {
UnauthenticatedQueryDefinition,
{=/ isAuthEnabled =}
_{= crud.entityUpper =},
} from "wasp/server/_types";
} from "../_types";
import type { Prisma } from "@prisma/client";
import { Payload } from "wasp/server/_types/serialization";
import type { Payload } from "../_types/serialization";
import type {
{= crud.entityUpper =},
} from "wasp/entities";
Expand Down Expand Up @@ -103,7 +103,12 @@ export type CreateActionResolved = typeof _waspCreateAction

{=# crud.operations.Update =}
{=^ overrides.Update.isDefined =}
type UpdateInput = Prisma.{= crud.entityUpper =}UpdateInput & Prisma.{= crud.entityUpper =}WhereUniqueInput
type UpdateInput = Prisma.XOR<
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixes UpdateInput to match what Prisma Client expects.

Prisma.{= crud.entityUpper =}UpdateInput,
Prisma.{= crud.entityUpper =}UncheckedUpdateInput
>
& Prisma.{= crud.entityUpper =}WhereUniqueInput

type UpdateOutput = _WaspEntity
export type UpdateActionResolved = {= crud.name =}.UpdateAction<UpdateInput, UpdateOutput>
{=/ overrides.Update.isDefined =}
Expand Down
45 changes: 23 additions & 22 deletions waspc/data/Generator/templates/server/src/crud/_operations.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
{{={= =}=}}
import { prisma } from 'wasp/server';
import { prisma } from 'wasp/server'

import type { Prisma } from "@prisma/client";
import type {
{= crud.entityUpper =},
} from "wasp/entities";
{=# isAuthEnabled =}
import { throwInvalidCredentialsError } from 'wasp/auth/utils'
{=/ isAuthEnabled =}
import type { {= crud.name =} } from "wasp/server/crud";
import type {
{=# crud.operations.GetAll =}
GetAllQueryResolved,
{=/ crud.operations.GetAll =}
{=# crud.operations.Get =}
GetQueryResolved,
{=/ crud.operations.Get =}
{=# crud.operations.Create =}
CreateActionResolved,
{=/ crud.operations.Create =}
{=# crud.operations.Update =}
UpdateActionResolved,
{=/ crud.operations.Update =}
{=# crud.operations.Delete =}
DeleteActionResolved,
{=/ crud.operations.Delete =}
} from 'wasp/server/crud/{= crud.name =}'
{=# overrides.GetAll.isDefined =}
{=& overrides.GetAll.importStatement =}
{=/ overrides.GetAll.isDefined =}
Expand All @@ -25,7 +37,6 @@ import type { {= crud.name =} } from "wasp/server/crud";
{=& overrides.Delete.importStatement =}
{=/ overrides.Delete.isDefined =}

type _WaspEntity = {= crud.entityUpper =}
const entities = {
{= crud.entityUpper =}: prisma.{= crud.entityLower =},
}
Expand All @@ -39,9 +50,7 @@ const entities = {
// 1. We either use the default implementation of the operation...
=}
{=^ overrides.GetAll.isDefined =}
type GetAllInput = {}
Copy link
Contributor Author

@infomiho infomiho Aug 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some reason, we built these types in two places: in the server and the in the SDK, but there was no need since the server can import the built types from the SDK. This change ensures that CRUD operation types are coming one a single source of truth.

type GetAllOutput = _WaspEntity[]
const _waspGetAllQuery: {= crud.name =}.GetAllQuery<GetAllInput, GetAllOutput> = ((args, context) => {
const _waspGetAllQuery: GetAllQueryResolved = ((args, context) => {
{=^ crud.operations.GetAll.isPublic =}
throwIfNotAuthenticated(context)
{=/ crud.operations.GetAll.isPublic =}
Expand Down Expand Up @@ -73,9 +82,7 @@ export async function getAllFn(args, context) {
{=# crud.operations.Get =}
// Get query
{=^ overrides.Get.isDefined =}
type GetInput = Prisma.{= crud.entityUpper =}WhereUniqueInput
type GetOutput = _WaspEntity | null
const _waspGetQuery: {= crud.name =}.GetQuery<GetInput, GetOutput> = ((args, context) => {
const _waspGetQuery: GetQueryResolved = ((args, context) => {
{=^ crud.operations.Get.isPublic =}
throwIfNotAuthenticated(context)
{=/ crud.operations.Get.isPublic =}
Expand All @@ -97,9 +104,7 @@ export async function getFn(args, context) {
{=# crud.operations.Create =}
// Create action
{=^ overrides.Create.isDefined =}
type CreateInput = Prisma.{= crud.entityUpper =}CreateInput
type CreateOutput = _WaspEntity
const _waspCreateAction: {= crud.name =}.CreateAction<CreateInput, CreateOutput> = ((args, context) => {
const _waspCreateAction: CreateActionResolved = ((args, context) => {
{=^ crud.operations.Create.isPublic =}
throwIfNotAuthenticated(context)
{=/ crud.operations.Create.isPublic =}
Expand All @@ -121,9 +126,7 @@ export async function createFn(args, context) {
{=# crud.operations.Update =}
// Update action
{=^ overrides.Update.isDefined =}
type UpdateInput = Prisma.{= crud.entityUpper =}UpdateInput & Prisma.{= crud.entityUpper =}WhereUniqueInput
type UpdateOutput = _WaspEntity
const _waspUpdateAction: {= crud.name =}.UpdateAction<UpdateInput, UpdateOutput> = ((args, context) => {
const _waspUpdateAction: UpdateActionResolved = ((args, context) => {
{=^ crud.operations.Update.isPublic =}
throwIfNotAuthenticated(context)
{=/ crud.operations.Update.isPublic =}
Expand All @@ -149,9 +152,7 @@ export async function updateFn(args, context) {
{=# crud.operations.Delete =}
// Delete action
{=^ overrides.Delete.isDefined =}
type DeleteInput = Prisma.{= crud.entityUpper =}WhereUniqueInput
type DeleteOutput = _WaspEntity
const _waspDeleteAction: {= crud.name =}.DeleteAction<DeleteInput, DeleteOutput> = ((args, context) => {
const _waspDeleteAction: DeleteActionResolved = ((args, context) => {
{=^ crud.operations.Delete.isPublic =}
throwIfNotAuthenticated(context)
{=/ crud.operations.Delete.isPublic =}
Expand Down
Loading
Loading