Open simonjoom opened 7 months ago
@simonjoom "Fixing" the type only hides the bug. GraphQL runtime does not perform "at least one" check. So you may end up in runtime error where Prisma yells about missing fields for unique.
So I would recommend check in resolver manually and TS should infer the values are checked and you can use spread to set where fields in prisma query properly without type issues.
This is aim to resolve the typings issue,.. which is shame to have a typescript generator if we cannot properly use typescript..
, i don't think i will have any runtime error after my change though, read the solution, i use Prisma.AtLeast for the 'at least one check ' Anyway in code we will never use more than one unique for a whereinput.. i dont see the relevance to check a whereinput with 2 unique... so if a runtime error occur it s only because the implementation is not correct.
this workaround allow to not see a typings issue on something not relevant anyway..
thks
This is a workaround, not a solution. With your "fix" you can bypass the typescript type check, as this check takes place only in compile time. In runtime you would then be able to send no data (as graphql definitions alows to do so), your code would have no errors passing those args into prisma client query, but you would end up with error thrown into face 😛
So yeah, while I agree there should be no type issues in generated code, I can't agree with the proposed solution. If you don't care about runtime errors and just want to silence the compiler, just place any
all over the code. This is the same quality workaround as proposed.
well I just dont want to use 'any' all over the code... what the point to have typescript else???
maybe to show that the workaround work pretty good for me i share below the code that i do use..
I want to say also that nestjs use the same workaround... i m not the only one.
here below a simplified version of a resolver code i use.. I do get all typings references from the generated typescript, the changes done in the generated code allow all this to work (added some atleast for all reference to a whereuniqueinput and connects like i explained upper).
As you can see the mutation here contains lot of complicated nested upsert ...etc and it s work, the query work in my tests and also in studio apollo. i didnt have any graphql errors at all... well maybe give me an example that graphql can throw an error?
If i dont apply this workaround then i could not use typescript to check my code is correctly written,..
//the resolver take argument update=true to make some modifications in database listings before to show all the listings/
//resultsArray is just a json object from algolia {client.initIndex("listings")} that i use to retrieve my data to reflect into prisma dtb
import {
Detail, DetailCreateInput, Listing, ListingUpdateInput, CategoryUpsertWithoutListingInput, ListingWhereUniqueInput, CategoryUpdateOneRequiredWithoutListingNestedInput, CategoryWhereUniqueInput, CategoryCreateOrConnectWithoutListingInput, Sub_CatUpdateOneRequiredWithoutListingNestedInput, FreelancerUpdateManyWithoutListingNestedInput, ListingCreateInput, ListingUpsertWithWhereUniqueWithoutSub_catInput,
FreelancerCreateOrConnectWithoutListingInput, ResortCreateOrConnectWithoutFreelancerInput, Sub_CatCreateOrConnectWithoutListingInput, CountryCreateOrConnectWithoutUserInput, UserCreateOrConnectWithoutFreelancerInput, LangueCreateOrConnectWithoutFreelancerInput
} from '../prisma/generated/type-graphql';
@Resolver()
export class ListingResolver {
@Query(_returns => [Listing], { nullable: true })
async getListings(@Ctx() ctx: ContextValue, @Info() info: GraphQLResolveInfo, @Arg('update', { nullable: true }) update: boolean): Promise<Listing[]> {
let Listings = [];
if (update) {
const { hits, nbHits, nbPages } = await client.initIndex("listings").search("", {})
let resultsArray: any = hits.map(hit => {
const { _highlightResult, ...el } = hit;
return el;
});
//below the function getattrobj just format deeply the json correctly
resultsArray = resultsArray.map(hit => getattrobj(hit, Object.keys(hit).sort(), false))
for (const e of resultsArray) {
const params = {
where: { Slug: e.Slug } as Prisma.AtLeast<ListingWhereUniqueInput, "id" | "Slug">,
create: {
Slug: e.Slug,
price: e.price as number,
description: e.description,
cover_image: e.cover_image ? e.cover_image : "",
category: {
connectOrCreate: {
where: { name: category.name },
create: {
name: category.name,
description: category.description,
cover_image: category.cover_image ? category.cover_image : ""
}
} as CategoryCreateOrConnectWithoutListingInput
},
sub_cat: {
connectOrCreate: {
where: { name: sub_cat.name },
create: {
name: sub_cat.name,
type: sub_cat.type,
Slug: sub_cat.Slug
},
} as Sub_CatCreateOrConnectWithoutListingInput
} as Sub_CatUpdateOneRequiredWithoutListingNestedInput
} as ListingCreateInput,
update: {
price: { set: e.price as number },
description: { set: e.description },
cover_image: { set: e.cover_image ? e.cover_image : "" },
category: {
upsert: {
where: { name: { equals: category.name } },
create: {
name: category.name,
description: category.description,
cover_image: category.cover_image
},
update: {
name: { set: category.name },
description: { set: category.description },
cover_image: { set: category.cover_image ? category.cover_image : "" }
}
} as CategoryUpsertWithoutListingInput
} as CategoryUpdateOneRequiredWithoutListingNestedInput,
sub_cat: {
connectOrCreate: {
where: { name: sub_cat.name },
create: {
name: sub_cat.name,
type: sub_cat.type,
Slug: sub_cat.Slug
},
} as Sub_CatCreateOrConnectWithoutListingInput
} as Sub_CatUpdateOneRequiredWithoutListingNestedInput
} as ListingUpdateInput
}
await ctx.prisma.listing.upsert({
...params
})
}
}
return await ctx.prisma.listing.findMany(); //return the listings saved in prisma
}
it s just a pain in the ass to repatch all the generated code as soon i need to change my schema
i didnt have any graphql errors at all... well maybe give me an example that graphql can throw an error?
Sure! 😉
/// @@TypeGraphQL.type(name: "MainUser")
model User {
id Int @id @default(autoincrement())
email String @unique
age Int
}
@Query(returns => MainUser)
async uniqueMainUser(
@Ctx() { prismaClient }: Context,
@Args() args: FindUniqueMainUserArgs,
): Promise<Prisma.User> {
return await prismaClient.user.findUniqueOrThrow(args);
}
query UniqueArgsFailing {
uniqueMainUser(where: {age: {gte: 18}}) {
id
email
}
}
Explanation:
The signature of of MainUserWhereUniqueInput
in GraphQL definition is that both id
and email
are optional. However, Prisma requires one of them to be present. So you can send "malformed" GraphQL query parameter, like where age gte 18, which is allowed by GraphQL, but Prisma throws an error.
Why your workaround is just about supressing the error? Because when we put Prisma.AtLeast
we just make TS happy. But GraphQL nor TypeGraphQL still does not know about this rule. So the fields are still optional, still you can execute query that will break Prisma, but your IDE does not have red underline and you live happy, thinking all good, while production is broken 😉
A proper solution would be to include a runtime check. GraphQL does not support input unions, there's some work on @oneOf
directive. So for now the only option is to do the check manually. Then TS compiler should now that one of those field is not null and allow to pass to Prisma Client.
Much complex but better solution would be to include those checks in generated resolvers, so that they will also work properly, without crashing Prisma.
i dont see again where my code can be wrong..
Yes i do understand when you say GraphQL does not support input unions.. then maybe prisma have to remove this "Prisma.atleast " ... if you dont want to follow their signature afterall prisma work with graphql right?
what you just wrote: "Much complex but better solution would be to include those checks in generated resolvers, so that they will also work properly, without crashing Prisma."
it s exactly what i did, i added Prisma.AtLeast in each generated resolvers (like Userwhereunique ..modelWhereunique ... that i use in code where exist a check on one unique , yes typescript is happy and the final code will remain the same.. (the mods dont change the compilation code) With the mods typescript prisma signature pass with typegraphql-prisma
there is as well one change that your generated code should do... if there is not any unique in the schema (other than id of course) then in any whereUniqueinput
id?:
should not be optional but instead:
id!:
of course to follow prisma typescript happy
I sustain my claim and I'm sorry that you don't understand the root issue.
I'm afraid that you need to use the fork/mod with your "workaround" until this issue will be fully solved in a proper way.
It might work for your limited use-case but I've pointed another query that fails no matter we "assert" the type by "fixing" the arg field TS type or by using as any
- the result is the same, Prisma throws runtime validation error.
Describe the Bug compatibility issue in generated code for prisma v5
Environment (please complete the following information):
typegraphql-prisma
last current version[see isssue] (https://github.com/unlight/prisma-nestjs-graphql/issues/177#issuecomment-2002730860) at the end one solution