registerSubclass doesn't work

I’m getting a plain ParseObject instead of the registered subclass as a result of queries. This is how I’m defining my class in Typescript:

import * as Parse from 'parse/node';

export interface MyClassAttributes {
  id: string;
  objectId: string;
  createdAt: Date;
  updatedAt: Date;
  someData: string;
}

export class MyClass extends Parse.Object<MyClassAttributes> {
  constructor (data?: Partial<MyClassAttributes>) {
    super('MyClass', data as MyClassAttributes);
  }
}

Parse.Object.registerSubclass('MyClass', MyClass);

Here’s my cloud function. The cloud function file is imported after the class definition, and the code is something like this:

import { MyType } from '../classes/MyType';

Parse.Cloud.define(
  'myFunction',
  async (request): Promise<MyDataInterface> => {
    const myObjectId = 'xxxxx'; // dynamic
    const query = new Parse.Query(MyType);
    let myInstance = await query.get(myObjectId, { useMasterKey: true });
    console.log(myInstance, myInstance.constructor.name);
});

When called, I get this as output:

ParseObject { _objCount: 21, className: 'MyClass', id: 'gO2Ru31MrP' } ParseObject

Any idea of what I could be doing wrong or how to cast to the proper class? Thank you

Are you defining MyClass also in the client? Can you share how you are calling the cloud code function and logging out the return?

Are you defining MyClass also in the client? Can you share how you are calling the cloud code function and logging out the return?

No parse client, the class is used internally and this cloud function is called through GraphQL @resolve.

extend type Query {
      myFunction: MyDataInterface! @resolve
}

The lack of casting happens in the cloud function.

As a workaround, I’m currently doing a manual recast like this:

let myInstance = await query.get(myObjectId, { useMasterKey: true });
myInstance = new MyClass(myInstance.toJSON() as any);

Can you share the code of ../classes/MyType?

Yes, this is it:

import * as Parse from 'parse/node';

type User = Parse.Object;

export interface MyClassAttributes {
  id: string;
  objectId: string;
  createdAt: Date;
  updatedAt: Date;
  userPointer: User;
  /// other fields
}

export class MyClass extends Parse.Object<MyClassAttributes> {
  constructor (data?: Partial<MyClassAttributes>) {
    super('MyClass', data as MyClassAttributes);
  }
}

Parse.Object.registerSubclass('MyClass', MyClass);

Are you sure? Because I see you exporting MyClass via export class MyClass and importing MyType via import { MyType } from '../classes/MyType';.

Sorry, I mixed MyType and MyClass as I anonymized the code. But on the real code I’m using the same identifier.

I guess that you have 2 parse js sdks installed and you are registering the class using one of them while Parse Server is running the cloud code function with other. Can you check your package.json if you have the parse js sdk installed maybe in a different version that your Parse Server is also requiring?

All parse related packages in package.json:

    "@parse/s3-files-adapter": "^1.6.2",
    "parse": "^3.4.0",
    "parse-dashboard": "^3.3.0",
    "parse-server": "^4.10.7",
    "parse-server-sendgrid-adapter-maroon-bells": "^1.0.0",
    "@openinc/parse-server-schema": "^1.3.2",
    "@types/parse": "^2.18.13",

But I think you gave me a path to understand the issue, which is likely to be the same root issue from Schema mismatch, expected Pointer - #4 by davimacedo.

If I do a console.log(Parse.Cloud); on my cloud/endpoints/main.ts I get this:

{
  run: [Function: run],
  getJobsData: [Function: getJobsData],
  startJob: [Function: startJob],
  getJobStatus: [Function: getJobStatus],
  useMasterKey: [Function (anonymous)],
  define: [Function (anonymous)],
  job: [Function (anonymous)],
  beforeSave: [Function (anonymous)],
  beforeDelete: [Function (anonymous)],
  beforeLogin: [Function (anonymous)],
  afterLogin: [Function (anonymous)],
  afterLogout: [Function (anonymous)],
  afterSave: [Function (anonymous)],
  afterDelete: [Function (anonymous)],
  beforeFind: [Function (anonymous)],
  afterFind: [Function (anonymous)],
  beforeSaveFile: [Function (anonymous)],
  afterSaveFile: [Function (anonymous)],
  beforeDeleteFile: [Function (anonymous)],
  afterDeleteFile: [Function (anonymous)],
  beforeConnect: [Function (anonymous)],
  beforeSubscribe: [Function (anonymous)],
  onLiveQueryEvent: [Function (anonymous)],
  afterLiveQueryEvent: [Function (anonymous)],
  _removeAllHooks: [Function (anonymous)],
  httpRequest: [Function: httpRequest] { encodeBody: [Function: encodeBody] }
}

if I do

import * as Parse from 'parse/node';
console.log(Parse.Cloud);

I get

  getJobStatus: [Function: getJobStatus],
  getJobsData: [Function: getJobsData],
  run: [Function: run],
  startJob: [Function: startJob],
  useMasterKey: [Function (anonymous)]
}

So apparently I’m mixing up server and client imports and that breaks stuff. This explains why I have problems with Jest, which includes some auxiliary server code directly but works as a client.

So if I’m sharing code between server and client, like with Jest, how should I do it?

After removing imports to parse/node everywhere on server, and using import * as ParseClient from 'parse/node'; on client code to make sure there’s no clash, I started getting Cannot use the Master Key, it has not been provided. on the cloud code.

The master key is set on new ParseServer({}). I also tried setting it directly with Parse.CoreManager.set('MASTER_KEY', process.env.MASTER_KEY); after the server initializes, same result. Any tips of how to allow useMasterKey on the server code?

Sorry about the flood. Here’s a summary of fixes:

  • removing all the import * as Parse from 'parse/node'; solved some of the issues.
  • Parse.initialize(process.env.APP_ID || '', undefined, process.env.MASTER_KEY); solved the master key problems.

The only remaining issue is with the classes. I’m still getting ReferenceError: Parse is not defined for them, perhaps because they are being imported before ParseServer starts. Any guidelines on how to define and import them on the server?

Parse Server 4.10.7 uses Parse 3.3.0 as dependency: parse-server/package.json at 7c844772eaf8f12e0d2c175360a1a553e113bc2c · parse-community/parse-server · GitHub

Because of this, I guess you have two versions of Parse installed, one in node_modules/parse, and other in node_modules/parse-server/node_modules/parse. Your cloud code is using the first while Parse Server is using the second. That’s why things are messed up.

I’d try to change "parse": "^3.4.0", to "parse": "3.3.0", or compeletely remove it, leaving only the one that Parse Server is already installing.