1
votes

I'm using TypeScript with NestJS on this project:

https://github.com/EricKit/nest-user-auth

I'm attempting to add the _id property to the GraphQL Schema:

type User {
  username: String!
  email: String!
  permissions: [String!]!
  createdAt: Date!
  updatedAt: Date!
  enabled: Boolean!
  _id: String!
}

Now, NestJS generate a type file for a user from this schema

export abstract class User {
    username: string;
    email: string;
    permissions: string[];
    createdAt: Date;
    updatedAt: Date;
    enabled: boolean;
    _id: string;
}

The problem now is I want to create an interface for a UserDocument that adds the mongoDB specific fields and defines a document

export interface UserDocument extends User, Document {
  // Declaring everything that is not in the GraphQL Schema for a User
  _id: string; // TODO: This should actually be Types.ObjectId
  password: string;
  lowercaseUsername: string;
  lowercaseEmail: string;
  passwordReset?: {
    token: string;
    expiration: Date;
  };

  checkPassword(password: string): Promise<boolean>;
}

I can't define the _id as a mongoose.Type.ObjectID because it produces the error:

Interface 'UserDocument' incorrectly extends interface 'User'.
  Types of property '_id' are incompatible.
    Type 'ObjectId' is not assignable to type 'string'.

This makes sense, I'd like to find a way to keep it as an ObjectId on my UserDocument schema, keep it as a String in the Schema, and still be able to extend the GraphQL schema type. Is this possible?

1

1 Answers

4
votes

GraphQL way is custom scalar type for serialize/deserialize ObjectId

GraphQL Schema

scalar Date
scalar MongoObjectId
...
type User {
  username: String!
  email: String!
  permissions: [String!]!
  createdAt: Date!
  updatedAt: Date!
  enabled: Boolean!
  _id: MongoObjectId!
}

MongoObjectId scalar class, inspired by NestJs Date scalar and TypeGraphQL ObjectId scalar

import { Scalar } from '@nestjs/graphql';
import { Kind, ASTNode } from 'graphql';
import { ObjectId } from "mongodb";

@Scalar('MongoObjectId')
export class ObjectIdScalar {
  description = 'Mongo object id scalar type';

  parseValue(value: string) {
    return new ObjectId(value); // value from the client
  }

  serialize(value: ObjectId) {
    return value.toHexString(); // value sent to the client
  }

  parseLiteral(ast: ASTNode) {
    if (ast.kind === Kind.STRING) {
      return new ObjectId(ast.value); // value from the client query
    }
    return null;
  }
}

Afterward, we need to register ObjectIdScalar as provider (just like with date scalar) and change _id from string to Type.ObjectID.