MichalLytek / typegraphql-prisma

Prisma generator to emit TypeGraphQL types and CRUD resolvers from your Prisma schema

Home Page:https://prisma.typegraphql.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

`_count` returning null from custom resolvers

art049 opened this issue · comments

Describe the Bug
When accessed through custom resolvers, the _count field is always null.

To Reproduce
Considering the following schema(I'm using mongo):

model List {
  id    String     @id @default(auto()) @map("_id") @db.ObjectId
  items ListItem[]
}

model ListItem {
  id     String @id @default(auto()) @map("_id") @db.ObjectId
  list   List   @relation(fields: [listId], references: [id])
  listId String @db.ObjectId
}

And this sample custom resolver:

import { List } from "@generated/type-graphql";
import { Ctx, Query, Resolver } from "type-graphql";
import { Context } from "..";

@Resolver()
export class MyListResolver {
  @Query(() => List)
  async mylist(@Ctx() { prisma }: Context): Promise<List> {
    return await prisma.list.findFirstOrThrow();
  }
}

With the following data:
image

This query:

query Mylist {
  mylist {
    id
    _count {
      items
    }
  }
}

Returns a null count:

{
  "data": {
    "mylist": {
      "id": "6408705c0cd690ec08e5b690",
      "_count": null
    }
  }
}

Expected Behavior
The query should return the actual count so:

{
  "data": {
    "mylist": {
      "id": "6408705c0cd690ec08e5b690",
      "_count": {
        "items": 2
      }
    }
  }
}

Environment (please complete the following information):

  • OS: Manjaro 22.0.4
  • Node lts/gallium
  • typegraphql-prisma version 0.24.2 (the issue was also there with 0.21.4)
  • Prisma version 4.11 (the issue was also there with 4.3.0)
  • TypeScript version 4.5.4

Additional Context
I'm using MongoDB with prisma.

This feature is only available for the generated resolvers.

It's barely possible to make it working automagically for custom resolvers under the hood.

Check out the body of the generated resolvers and implement it in your custom resolvers:

async findFirstCategory(@Ctx() ctx: any, @Info() info: GraphQLResolveInfo, @Args(_returns => FindFirstCategoryArgs) args: FindFirstCategoryArgs): Promise<Category | null> {
  const { _count } = transformInfoIntoPrismaArgs(info);
  return getPrismaFromContext(ctx).category.findFirst({
    ...args,
    ...(_count && transformCountFieldIntoSelectRelationsCount(_count)),
  });
}

You need to first read and parse the graphql resolve info to get into about requested fields in query. And then build a proper prisma args for fetching count for relations.

Okay, I thought this was an unexpected behavior.
Wouldn't it be possible to add this through the Query decorator?
Btw, I'd be up working on this if you think it's doable

It's not possible to achieve automagically via the @Query decorator.

The crucial part here is that we can't move this logic of transformInfoIntoPrismaArgs and transformCountFieldIntoSelectRelationsCount into relation resolvers (virtual _count filed working with all queries, even custom) because Prisma relations count is coupled with the main, root db query:
https://www.prisma.io/docs/concepts/components/prisma-client/aggregation-grouping-summarizing#count-relations

So that's why the crud resolver logic is reading gql query and apply _count part of the prisma query there.
If we moved them to the relation resolvers, we would be missing args from the graphql query, so all the filtration would be not taken into account.

Automagic decorator also won't automagically put the _count into your prisma.xyz.find() call.
All you could do is to create custom param decorator that contains all the logic, so you just get _count variable to put directly in your query.

OK now I see, we can make this working tho less optimal from query perspective 😉

@Resolver()
class UserResolver {
  @Query(() => [MainUser])
  async users(@Ctx() { prismaClient }: Context) {
    return prismaClient.user.findMany();
  }
}

@Resolver(() => MainUser)
class UserRelationsResolver {
  @FieldResolver(() => MainUserCount)
  async _count(
    @Root() root: MainUser,
    @Ctx() { prismaClient }: Context,
    @Info() info: GraphQLResolveInfo,
  ) {
    const _count = transformInfoIntoPrismaArgs(info);
    const data: any = await prismaClient.user.findUniqueOrThrow({
      where: { id: root.id },
      ...(_count && transformCountFieldIntoSelectRelationsCount(_count)),
    });
    return data._count;
  }
}

Nice! Thanks.