DEV Community

Mehrad Rousta
Mehrad Rousta

Posted on

Nested Queries in Prisma Client

These days I'm working on a new project in the workplace which is the first project I work on that is built on Prisma and Yoga. I worked with Prisma before on the days that the only way to access Prisma was Prisma Bindings. but at this new project, we were using Prisma Client.

Imagine this project we have here is a social media app with these models:

type User {
  id: ID! @id
  email: String! @unique
  password: String!
  username: String @unique
  fullName: String!
  role: [UserRole]!
  settings: UserSetting @relation(name: "UserSettings" link: INLINE)
  following: [User] @relation(name: "UserFollowings" link: INLINE)
  createdAt: DateTime! @createdAt
  updatedAt: DateTime! @updatedAt
}

As it's mentioned here and here, with Prisma Bindings we could use something like this for our queries to get all the nested queries we wanted:

// Prisma Bindings
me(parent, args, { prisma, request }, info) {
  const userId = getUserId(request)
  return prisma.query.user({
    where: {
      id: userId
    },
    info
  });
}

which by passing info, the Prisma query would notice what fields it must fetch from the database.

but when I was looking in my code, my Prisma query only had one parameter which was the filter/sort object. so I handled it this way:

// Prisma Client
 async user(_, __, ctx: IContext) {
    const user = await ctx.db.user({ id: ctx.user.id });
    // Getting userSettings because apparently `ctx.db.user` won't give it
    const [userSettings] = await ctx.db.userSettings({ where: { user: { id: ctx.user.id } } });
    return { ...user, settings: userSettings };
  },

But there is a problem, this method is ok on single queries but won't work on list queries:

  async users(_, __, ctx: IContext) {
    return await ctx.db.query.users();
  },

In my front-end, when I was using this users query, I wasn't getting for example user.following relational data.

the workaround for this is $fragment API explained in Prisma docs:

Instead of querying all scalar fields of a record (which is the default behavior), you can specify which fields you'd like to retrieve by using the $fragment API. This is useful to exclude large unneeded fields (like BLOB values or huge strings). It also lets you fetch arbitrary relations.

so the code would be rewritten like this:

async users(_, __, ctx: IContext) {
  const fragment = `
    fragment UserWithFollowings on User {
      id
      fullName
      email
      following {
        id
        email
      }
    }
  `;
  return await ctx.db.query.users().$fragment(fragment);
},

The docs is saying there will be a next version of the Prisma client API that has an improved and type-safe API to select fields but I couldn't find more information about it. if you have any information about it and want to share, i would be very happy!

Discussion (2)

Collapse
dannytlake profile image
dannytlake

I'm curious on how you'd use a fragment combined with a filter? I am new and reading the docs, but it seems to me that with the Prisma client you can either use a filter, or a fragment, but not both at the same time?

Collapse
sturmenta profile image
sturmenta