4

I have a basic Nestjs - Mongoose - Graphql api with I have two schemas defined: User and Event

//USER Schema
@Schema()
export class User extends Document {
  @Prop()
  username: string;

  @Prop({ required: true })
  password: string;

  @Prop({ required: true, unique: true })
  email: string;

  @Prop({ required: true, unique: true })
  handle: string;

  @Prop()
  avatar: string;
}

export const UserSchema = SchemaFactory.createForClass(User);
//EVENT schema
@Schema()
export class Event extends Document {
  @Prop({
    type: MongooseSchema.Types.ObjectId,
    ref: User.name,
    required: true,
  })
  creator: GqlUser;

  @Length(5, 30)
  @Prop({ required: true })
  title: string;

  @Length(5, 200)
  @Prop({ required: true })
  description: string;

  @Prop()
  createdDate: string;

  @Prop()
  public: boolean;

  @Prop()
  active: boolean;
}

export const EventSchema = SchemaFactory.createForClass(Event);

In the EventSchema, field creator is typed as MongooseSchema.Types.ObjectId pointing at User

My events.resolvers.ts looks like this:


@Resolver(of => GqlEvent)
export class EventsResolvers {
  constructor(private eventsService: EventsService) {}

  @Query(returns => [GqlEvent])
  async events() {
    return this.eventsService.findAll();
  }

  @Mutation(returns => GqlEvent)
  async createEvent(
    @Args('createEventInput') createEventInput: CreateEventDto,
  ) {
    return this.eventsService.create(createEventInput);
  }
}

event Dtos :

@ObjectType()
export class GqlEvent {
  @Field(type => ID)
  id: string;

  @Field(type => GqlUser)
  creator: GqlUser;

  @Field()
  title: string;

  @Field()
  description: string;

  @Field()
  createdDate: string;

  @Field()
  public: boolean;

  @Field()
  active: boolean;
}

@InputType()
export class CreateEventDto {
  @Field(type => ID)
  creator: GqlUser;

  @Field()
  @Length(5, 30)
  title: string;

  @Field()
  @Length(5, 200)
  description: string;

  @Field()
  @IsBoolean()
  public: boolean;
}

With such, Nestjs generates following gql schema (I skip the parts related to user CRUD for clarity) :

# ------------------------------------------------------
# THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY)
# ------------------------------------------------------

type GqlUser {
  id: ID!
  username: String!
  handle: String!
  avatar: String!
  email: String!
}

type GqlEvent {
  id: ID!
  creator: GqlUser!
  title: String!
  description: String!
  createdDate: String!
  public: Boolean!
  active: Boolean!
}

type Query {
  events: [GqlEvent!]!
}

type Mutation {
  createEvent(createEventInput: CreateEventDto!): GqlEvent!
}


input CreateEventDto {
  creator: ID!
  title: String!
  description: String!
  public: Boolean!
}

What works : the createEvent mutation correctly inserts a document in database :

{
"_id":{"$oid":"5f27eacb0393199e3bab31f4"},
"creator":{"$oid":"5f272812107ea863e3d0537b"},
"title":"test event",
"description":"a test description",
"public":true,
"active":true,
"createdDate":"Mon Aug 03 2020",
"__v":{"$numberInt":"0"}
}

My problem : I have the following error when I try to request subfields of creator :

Gql query :

query {
  events {
    id
    creator {
      id
    }
    createdDate
    public
    description
    title
    active
  }
}

Response :

"errors": [
    {
      "message": "ID cannot represent value: <Buffer 5f 27 28 12 10 7e a8 63 e3 d0 53 7b>",
      "locations": [
        {
          "line": 6,
          "column": 7
        }
      ],
      "path": [
        "createEvent",
        "creator",
        "id"
      ],
      "extensions": {
        "code": "INTERNAL_SERVER_ERROR",
        "exception": {
          "message": "ID cannot represent value: <Buffer 5f 27 28 12 10 7e a8 63 e3 d0 53 7b>",
          "stacktrace": [
            "GraphQLError: ID cannot represent value: <Buffer 5f 27 28 12 10 7e a8 63 e3 d0 53 7b>",...

As it works fine when I omit creator field, I understand the mongoose MongooseSchema.Types.ObjectId causes problem with gql schema... but I could not find the appropriate way to fix it. Thx in advance for help

Pierre_T
  • 1,094
  • 12
  • 29
  • This is because `ObjectID` from mongoose, you have to somewhere cast it to string, like `id.toString()` then it will works fine. – cojack Aug 04 '20 at 08:28
  • Got the same error fixed it by adding Field Resolvers in the resolver. You can check this doc for more details https://typegraphql.com/docs/resolvers.html#field-resolvers – Shamin Meerankutty Nov 17 '20 at 19:39

2 Answers2

6

It actually had to to with the fact that creator field was not populated.

Changing from

async findAll(): Promise<Event[]> {
    return this.eventModel
      .find()
      .exec();
  }

to

async findAll(): Promise<Event[]> {
    return this.eventModel
      .find()
      .populate('creator')
      .exec();
  }

Fixed my problem. The error message was kind of misleading.

Pierre_T
  • 1,094
  • 12
  • 29
0

Make sure to populate the field that contains the id you want to display.