skip to Main Content

I’m trying to store a session on disk for local development, and I’m running into a problem where the application expects me to return an instance of a Session and not an Object

function storeCallback(session) {
  console.log("storeCallback ", session);
  fs.writeFileSync("./session.json", JSON.stringify(session));
  return true;
}

function loadCallback(id) {
  console.log("loadCallback ", id);
  const session = fs.readFileSync("./session.json", "utf8");
  return JSON.parse(session);
}

The session look like this:

Session {
   id: '0984e7db-ad80-4276-966d-9db54fac11c2',
   shop: 'swiftnextstep.myshopify.com',
   state: '113617456401679',
   isOnline: true
 }

Once I save and read back, it looks like this

 {
   id: '0984e7db-ad80-4276-966d-9db54fac11c2',
   shop: 'swiftnextstep.myshopify.com',
   state: '113617456401679',
   isOnline: true
 }

And I get an error InternalServerError: Expected return to be instance of Session, but received instance of Object.

How can I cast/transform the object I’m parsing from storage into the expected Session type?

3

Answers


  1. JSON does not save a class, so what you get back when you read JSON is just a plain object with properties. Usually, the way you reconstitute it into a class is that you create a constructor for your class that accepts that exact object you parsed from your JSON as a parameter to the constructor and then the constructor copies that data into the instance data for an instance of your class. Or, if you don’t control the constructor, then you use whatever constructor there is, create a new object of that type, then set the properties you need to set from the object you parsed from your JSON.

    It’s possible to directly set the prototype on the object you got from parsing the JSON with Object.setPrototypeOf(), but I prefer using the constructor design because then if you have other properties or newer properties in a newer version of the code, that constructor can programmatically handle any missing properties with appropriate defaults. It’s also often useful in your JSON to store a version number so if you change the format over time, you can know what version you’re reading from disk and can have code that adapts to the older or newer version of JSON.

    Note, I also don’t usually directly JSON.stringify() my class either because I may have state variables that don’t really belong in the JSON. Instead, I create a .toJSON() method and let that generate the JSON for me and then my code can control exactly what is and isn’t in the JSON from my instance data.

    Login or Signup to reply.
  2. JavaScript doesn’t really have nominative types for object values. It does have prototypes which are reified (through Object.getPrototypeOf and Constructor.prototype) as well as with instanceof. It looks like Shopify’s library is using instanceof or getProtoypeOf to determine if your parameter argument is a "Session" or not.

    Now, I’m not familiar with Shopify’s library, but you can use the Session constructor – or create a clone of the object (which carries-over the prototype). Try this:

    function loadCallback(id) {
      console.log("loadCallback ", id);
      const sessionJson = fs.readFileSync("./session.json", "utf8");
      const sessionObj  = new Session(); // assuming the ctor has no parameters
      
      const session2 = Object.assign( sessionObj, sessionJson ); // copy `sessionJson`'s properties into `sessionObj`.
    
      return session2;
    }
    
    Login or Signup to reply.
  3. Session looks to be exported only from session.ts. The constructor is empty, so that’s not something you have to worry about, it’s just an object with properties. To turn a non-Session into a Session, you should be able to just assign to properties of a Session.

    import { Session } from "@shopify/shopify-api/dist/auth/session/session";
    
    function loadCallback(id) {
      console.log("loadCallback ", id);
      return Object.assign(
        new Session(),
        JSON.parse(fs.readFileSync("./session.json", "utf8")))
      );
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search