Проблема с запросом связанных данных на 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.

Спасибо!

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Что такое Apollo Client и зачем он нужен?
Что такое Apollo Client и зачем он нужен?
Apollo Client - это полнофункциональный клиент GraphQL для JavaScript-приложений, который упрощает получение, управление и обновление данных в...
1
0
67
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 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   
    }
  }
}

ЛЕГЕНДА, да, в этом и была проблема. Я добавлял «Комментарий» в качестве запроса верхнего уровня вместо «Пользователь», я ценю ваше время! Большое спасибо!

CodeConnoisseur 29.03.2024 15:06

Другие вопросы по теме