Проблема с запросом связанных данных на Apollo Server 4 с помощью Prisma Schema

Я использую Apollo Server 4 и Prisma 5 для создания простого проекта ECOM. В настоящий момент я пытаюсь настроить свои преобразователи так, чтобы они могли двунаправленно запрашивать пользователей + их комментарии... а также запрашивать комментарии как верхний уровень и возвращать связанных с ними пользователей. В базе данных таблица Comment имеет связанное отношение user_id FK с таблицей User. User таблица НЕ имеет идентификатора комментариев, поэтому связь один -> многие и многие -> один.

Когда я пытаюсь запросить пользователя, комментарии — это null, если только я не напишу для них отдельный преобразователь, а затем не использую форму Prisma для include отношений. Я не хочу следовать этому, поскольку при использовании gql это похоже на антишаблон. Чего я хочу добиться, так это иметь userByEmail() преобразователь на моем верхнем уровне Query{} из моего SDL... а затем иметь объект Comment: {} 2-го уровня в моих преобразователях, чтобы затем соответствующий User был аргументом parent, поступающим в преобразователь... это больше зависит от GQL, чем от БД.

Соответствующие модели призмы:

model User {
  id          String        @id @default(uuid())
  username    String        @unique
  email       String        @unique
  password    String
  first_name  String
  last_name   String?
  created_at  DateTime      @default(now())
  updated_at  DateTime      @updatedAt
  deleted     Boolean       @default(false)
  comments    Comment[]
  ratings     Rating[]
  orders      Order[]
  UserAddress UserAddress[]
}


model Comment {
  id         String        @id @default(uuid())
  user       User     @relation(fields: [user_id], references: [id])
  user_id    String   // Change type to String
  product    Product  @relation(fields: [product_id], references: [id])
  product_id String
  content    String
  created_at DateTime @default(now())
  updated_at DateTime @updatedAt
  deleted    Boolean  @default(false)
}

Соответствующие типы Schema.graphql:

type Query {
  "Get a user by their email"
  userByEmail(email: String): User!
}

"This type represents a user in the system"
type User {
  "Unique identifier for the user"
  id: ID!

  "Username for login"
  username: String!

  "User's email address"
  email: String!

  "User's first name"
  first_name: String!

  "User's last name (optional)"
  last_name: String!

  "List of comments authored by the user (optional)"
  comments: [Comment]

  "List of ratings created by the user (optional)"
  ratings: [Rating]

  "List of orders placed by the user (optional)"
  orders: [Order]

  "List of user's addresses"
  addresses: [Address]
}

type Comment {
  "Unique identifier for the comment"
  id: ID!

  "Content of the comment"
  content: String!

  "User who authored the comment"
  user: User

  "Product this comment is associated with"
  product: Product
}

резольверы.ts

import { getUserByEmail, getCommentsByUserId } from '../../dataAccessLayer/orm/user';
import { User } from '../../generated/types';

// Use intersection type to combine Resolvers and Query
export const resolvers = {
  Query: {
    userByEmail: async (_parent: unknown, { email }: { email: string }) => {
      console.info('parent', email);
      const user = await getUserByEmail(email); // Already includes comments
      return user;
    },
  },
  Comment: {
    userComments: (parent: User) => {
      console.info('parent', parent);
      const userId = parent.id;
      return getCommentsByUserId(userId);
    },
  },
};

Запросы к базе данных ORM

import prisma from '../prismaClient';

export async function getUserByEmail(email: string) {
  return await prisma.user.findUnique({
    where: {
      email,
    },
  });
}

export async function getCommentsByUserId(id: string) {
  return await prisma.comment.findMany({
    where: {
      user_id: id,
    },
    include: {
      user: true, // Include user relation
    },
  });
}

Когда я запускаю свой проект, я получаю эту ошибку:

 Error: Comment.userComments defined in resolvers, but not in schema
[0]     at addResolversToSchema

Как я могу правильно запросить подполя, используя рекомендованный Apollo подход с использованием аргументов (parent, args, context, info), где parent должен быть связанный пользователь, первоначально запрашиваемый при попытке получить комментарии? Таким образом, я не объединяю запросы к базе данных на уровне orm.

Спасибо!


1
67
1

Ответ:

Решено

Взгляните на примеры в документации сервера Apollo, как реализовать преобразователи

В схеме вы уже включили свойство comments в тип User.

type User {
  id: ID!
  username: String!

  "List of comments authored by the user (optional)"
  comments: [Comment]

  # ... other fields
}

вы также определили userByEmail для получения пользователя типа верхнего уровня Query

type Query {
  userByEmail(email: String): User!
}

Далее вам необходимо реализовать преобразователи для этой схемы. Для типа Query вам необходимо реализовать преобразователь для метода userByEmail. Вы сделали эту часть

import { getUserByEmail } from '../../dataAccessLayer/orm/user';
import { User } from '../../generated/types';

// Use intersection type to combine Resolvers and Query
export const resolvers = {
  Query: {
    userByEmail: async (_, { email }: { email: string }) => {
      console.info('parent', email);
      const user = await getUserByEmail(email); // Already includes comments
      return user;
    },
  },
};

Следующее, что вам нужно сделать, это определить преобразователь для свойства comments объекта User, который даст вам именно то, что вы хотите: комментарии пользователей.

import { getUserByEmail, getCommentsByUserId } from '../../dataAccessLayer/orm/user';
import { User } from '../../generated/types';

// Use intersection type to combine Resolvers and Query
export const resolvers = {
  Query: {
    userByEmail: async (_, { email }: { email: string }) => {
      const user = await getUserByEmail(email); // Already includes comments
      return user;
    },
  },
  // User. not Comment. 
  User: {
    // The parent entity for comments field is user
    comments: async (parent: User, _: any) {
      if (parent.comments !== undefined) {
         // you mentioned that user fetched with userByEmail already 
         // includes comments, so you might not even need to refetch it
         return parent.comments 
      } 
      const comments = await getCommentsByUserId(user.id)
      return comments;
    },
  }
};

А это пример запроса, совместимого с нашей схемой

query userWithComments($email: String!) {
  userByEmail(email: $email) {
    id
    username
    # some other fields
    comments {
      id
      content   
    }
  }
}