How to write nested/relation/connection/subfield resolvers?

prisma

#1

I’m wondering if there’s a more comprehensive guide to common patterns for writing resolvers, especially for relations? I’ve looked at the article about the info object but it hasn’t helped me resolve this issue (no pun intended). I’m using Prisma’s Node.js boilerplate project, and can’t seem to get the right query response…

// DATA SCHEMA
type Car @model {
  createdAt: DateTime!
  id: ID! @unique
  manufacturer: Manufacturer @relation(name: "CarManufacturer")
  status: String
  user: User @relation(name: "CarUser")
}
type Manufacturer @model {
  createdAt: DateTime!
  id: ID! @unique
  updatedAt: DateTime!
}
type User @model {
  createdAt: DateTime!
  id: ID! @unique
  updatedAt: DateTime!
}
// CLIENT QUERY
// I've run this query both with the GraphQL playground and through Apollo in React; same results with both.
query CarsForUser($userId: ID!) {
  carsForUser(userId: $userId) {
    createdAt
    id
    manufacturer {
      id
    }
    status
    updatedAt
    user {
      id
    }
  }
}
// APPLICATION SCHEMA
type Query {
  car(carId: ID!): Car
  carByManufacturer(manufacturerId: ID!): Car
  cars: [Car]
  carsForUser(userId: ID!): [Car]
  manufacturer(manufacturerId: ID!): Manufacturer
  manufacturers: [Manufacturer]
  user(userId: ID!): User
  users: [User]
}
// QUERY LOGGED BY SERVER
// Note — the subfields (manufacturer, user) are being swallowed
query ($_v0_where: CarWhereInput) {
  cars(where: $_v0_where) {
    createdAt
    id
    status
    updatedAt
  }
}
// RESOLVER
Query: {
  ...
  carsForUser(parent, { userId }, ctx, info) {
    return ctx.db.query.cars({
      where: { 
        user: { id: userId },
      },
      info
    })
  }
}
// RESPONSE
{
  "cars": [
    {
      "createdAt": "2018-06-13T06:13:30.901Z",
      "id": "cjicpyv191aea0b73tub0vphx",
      "status": "new",
      "updatedAt": "2018-06-13T06:13:30.902Z",
    }
  ]
}
// DESIRED RESPONSE
{
  "cars": [
    {
      "createdAt": "2018-06-13T06:13:30.901Z",
      "id": "cjicpyv191aea0b73tub0vphx",
      "status": "new",
      "manufacturer": {
        "__typename": "Manufacturer",
        "id": "foo"
      },
      "updatedAt": "2018-06-13T06:13:30.902Z",
      "user": {
        "__typename": "User",
        "id": "bar"
      },
    }
  ]
}

#2

From what you shared, you’re doing everything correctly.

  1. What is the verbatim query sent to the Prisma API for the client query you shared?

    That query is printed to stdout when executed, if debug is set to true in the GraphQLServer constructor of prisma-binding.

  2. How is the Car type defined in your application schema?

  3. Is your code available somewhere publically?

    Being able to reproduce this behaviour locally makes it easier to see what is going on :slight_smile:

Thanks!


#3

Nilan—Thanks for replying! I know you have a busy job responding to all of us eager developers and we appreciate it. :slight_smile: Seriously!

I updated the post with the requested information. Unfortunately the code isn’t public, but I can try to spin up an instance later today that is.

It looks like the subfields are being swallowed on the query. This only happens with this particular query.


#4

Awesome, thanks for adding the information, that’s super useful! I changed the formatting to get a better overview :smiley:

I have some ideas what could happen, can you check the following?

  1. Can you share the entire application schema? In particular, if/how you are importing the Car type.
  2. If you are importing the Car type, can you share it from the generated schema for the Prisma API (usually called prisma.graphql, depending on your .graphqlconfig.yml). In particular, does it include the manufacturer field?
  3. When running queries against the Prisma API in the Playground directly, is the manufacturer field exposed on the Car type? When running the query, is the result the expected one?

Thanks! :slight_smile:


#5

I’ve uploaded a repo which demonstrates/should reproduce the issue:

  1. The whole schema is there. I’m using the # import statement at the top of the application schema.

  2. The generated Car type:

type Car implements Node {
  manufacturer(where: ManufacturerWhereInput): Manufacturer
  createdAt: DateTime!
  id: ID!
  status: String
  updatedAt: DateTime!
  user(where: UserWhereInput): User
}
  1. Yes, at least when querying for all Cars or for a Car by ID.

I will add that when logging out ctx in my resolver function, the original http request body includes the correct query, with all subfields specified.

It’s only the queries with relations as where input that don’t return properly, which makes me think it’s the resolvers — but i’m flatly confused! :slight_smile:


#6

Oi, I feel pretty silly but it was just a syntax error that was causing the problem:

// OLD RESOLVER
// Syntax error, swallowed nested fields
carsForUser(parent, { userId }, ctx, info) {
  return ctx.db.query.cars({
    where: { 
      user: { id: userId },
    },
    info
  })
}

// NEW RESOLVER
// Works properly
carsForUser(parent, { userId }, ctx, info) {
  return ctx.db.query.cars(
    {
      where: { 
        user: { id: userId },
      },
    },
    info
  )
}

I had accidentally merged the where and info into a single object in the return— ({ where, info }) — whereas it should be ({ where }, info)

I was driving myself mad, but I’m glad it was just a little syntax error in the end. Thanks for your help!


#7

Thank you so much for this thread! I have been struggling with the exact same problem, and this was the solution :smiley:

I wish there was a way that prisma could catch this error. I tried everything I could think of, but this mistake is very easy to miss, and you get no indications of why it happens.