GroupBy Argument Type required parameter follows an optional parameter
Hey,
the required property by follows the optional parameters where and orderBy, which results in an error on the generated typescript definition file from NestJs.
Example to reproduce:
// Prisma Schema
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
blogPosts BlogPost[]
@@map("user")
}
model BlogPost {
id Int @id @default(autoincrement())
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @updatedAt @map(name: "updated_at")
title String
content String?
published Boolean @default(false)
viewCount Int @default(0) @map(name: "view_count")
author User? @relation(fields: [authorId], references: [id])
/// @HideField()
authorId Int? @map(name: "author_id")
@@map("blog_post")
}
// NestJs Query
@Query(() => [UserGroupBy])
private async groupByUser(
@Args() groupByArgs: UserGroupByArgs,
): Promise<UserGroupBy[]> {
return this._user.groupBy(groupByArgs) as Promise<UserGroupBy[]>;
}
You will get following error in generated typescript definitions file:
error TS1016: A required parameter cannot follow an optional parameter.
abstract groupByUser(where?: Nullable<UserWhereInput>, orderBy?: Nullable<UserOrderByWithAggregationInput[]>, by: UserScalarFieldEnum[], having?: Nullable<UserScalarWhereWithAggregatesInput>, take?: Nullable<number>, skip?: Nullable<number>, _count?: Nullable<UserCountAggregateInput>, _avg?: Nullable<UserAvgAggregateInput>, _sum?: Nullable<UserSumAggregateInput>, _min?: Nullable<UserMinAggregateInput>, _max?: Nullable<UserMaxAggregateInput>): UserGroupBy | Promise<UserGroupBy>;
My versions:
- NestJs version: 11
- Prisma version: 6
- prisma-nestjs-graphql: 21.0.2
Some generated graphql classes (input/output) may not be compatible with prisma types. You must adapt it.
It's not the problem, that some generated graphql classes are not compatible with prisma types.
The fields of the generated @ArgType for the groupBy method have an incorrect order. The required fields should be defined before the optional fields.
Current generated class:
@ArgsType()
export class UserGroupByArgs {
@Field(() => UserWhereInput, { nullable: true })
@Type(() => UserWhereInput)
where?: InstanceType<typeof UserWhereInput>;
@Field(() => [UserOrderByWithAggregationInput], { nullable: true })
orderBy?: Array<UserOrderByWithAggregationInput>;
@Field(() => [UserScalarFieldEnum], { nullable: false }) // <-- Required field after optional fields
by!: Array<`${UserScalarFieldEnum}`>;
@Field(() => UserScalarWhereWithAggregatesInput, { nullable: true })
having?: InstanceType<typeof UserScalarWhereWithAggregatesInput>;
@Field(() => Int, { nullable: true })
take?: number;
@Field(() => Int, { nullable: true })
skip?: number;
@Field(() => UserCountAggregateInput, { nullable: true })
_count?: InstanceType<typeof UserCountAggregateInput>;
@Field(() => UserAvgAggregateInput, { nullable: true })
_avg?: InstanceType<typeof UserAvgAggregateInput>;
@Field(() => UserSumAggregateInput, { nullable: true })
_sum?: InstanceType<typeof UserSumAggregateInput>;
@Field(() => UserMinAggregateInput, { nullable: true })
_min?: InstanceType<typeof UserMinAggregateInput>;
@Field(() => UserMaxAggregateInput, { nullable: true })
_max?: InstanceType<typeof UserMaxAggregateInput>;
}
Class as it should be generated:
@ArgsType()
export class UserGroupByArgs {
@Field(() => [UserScalarFieldEnum], { nullable: false }) // <-- Required field at first
by!: Array<`${UserScalarFieldEnum}`>;
@Field(() => UserWhereInput, { nullable: true })
@Type(() => UserWhereInput)
where?: InstanceType<typeof UserWhereInput>;
@Field(() => [UserOrderByWithAggregationInput], { nullable: true })
orderBy?: Array<UserOrderByWithAggregationInput>;
@Field(() => UserScalarWhereWithAggregatesInput, { nullable: true })
having?: InstanceType<typeof UserScalarWhereWithAggregatesInput>;
@Field(() => Int, { nullable: true })
take?: number;
@Field(() => Int, { nullable: true })
skip?: number;
@Field(() => UserCountAggregateInput, { nullable: true })
_count?: InstanceType<typeof UserCountAggregateInput>;
@Field(() => UserAvgAggregateInput, { nullable: true })
_avg?: InstanceType<typeof UserAvgAggregateInput>;
@Field(() => UserSumAggregateInput, { nullable: true })
_sum?: InstanceType<typeof UserSumAggregateInput>;
@Field(() => UserMinAggregateInput, { nullable: true })
_min?: InstanceType<typeof UserMinAggregateInput>;
@Field(() => UserMaxAggregateInput, { nullable: true })
_max?: InstanceType<typeof UserMaxAggregateInput>;
}
Weird. 😕
Like I suspected, order of fields in class doesnt matter.
Some types may not be compatible, in your case with schema above generated UserGroupByArgs is compatible with prisma user.groupBy argument.
import { Prisma } from '@prisma/client';
@Query(() => [UserGroupBy])
async groupByUser(
@Args() groupByArgs: UserGroupByArgs,
): Promise<UserGroupBy[]> {
const userGroupByArgs: Prisma.UserGroupByArgs = groupByArgs; // no errors
return prisma.user.groupBy(userGroupByArgs);
}
Tested on graphql query
query {
groupByUser(
orderBy: [{ name: asc }]
by: [id, name]
)
{
id
name
}
}
userGroupByArgs = {
orderBy: [ { name: 'asc' } ],
by: [ 'id', 'name' ]
}
I created a minimal reproduction project. You can find it here.
I noticed that I used that the definitions property to configure the GraphQLModule, but it is not necessary in code-first approach. The error error TS1016: A required parameter cannot follow an optional parameter. occurred in this generated definitions file.
However, I get still an error on the groupBy function:
@Query(() => [UserGroupBy])
async groupByUser(
@Args() groupByArgs: UserGroupByArgs,
): Promise<UserGroupBy[]> {
const userGroupByArgs: Prisma.UserGroupByArgs = groupByArgs;
// TODO: Does not work without @ts-ignore
return this._prisma.user.groupBy(userGroupByArgs);
}
You can search for TODO in my example project.
Looks like you are facing this issue https://github.com/prisma/prisma/issues/17297
I see different error:
Type of property 'AND' circularly references itself in mapped type '{ [K in keyof { AND?: BlogPostScalarWhereWithAggregatesInput | BlogPostScalarWhereWithAggregatesInput[]; ... 9 more ...; authorId?: number | IntNullableWithAggregatesFilter<...>; }]: Or<...> extends 1 ? { ...; }[K] extends infer TK ? GetHavingFields<...> : never : {} extends FieldPaths<...> ? never : K; }'