0

I'm writing a simple application using a repository pattern, nodejs, graphql and sequelize. First here is my base repository:

import db from '../models';

export default class AbstractRepository {
  constructor (model, include) {
    if (new.target === AbstractRepository) {
      throw new TypeError('Cannot construct AbstractRepository instances directly.');
    }
    this.model = model;
    this.include = include;
  }
  static findAll () {
    return db[this.model].findAll({ include: this.include });
  }
}

Here is a repository that extends it:

import db from '../models';
import AbstractRepository from './AbstractRepository';

class UserRepository extends AbstractRepository {
  constructor () {
    super('User', [db.Clickwrap]);
  }
}

export default new UserRepository();

Lastly, here is where the UserRespository is used (there's more to this file, but this is the important part):

import UserRepository from '../../repositories/UserRepository';

const resolvers = {
  Query: {
    users (root, args, context) {
      return UserRepository.findAll();
    },
  },
};

When I run a simple graphql query in graphiql like this:

{
  users {
    id
  }
}

I get this error:

_UserRepository2.default.findAll is not a function

Not sure what I'm missing that's causing this.

Edit

I've updated my code so it's not exporting an instance anymore. Everything else has remained the same. Here's my new UserRepository file:

import db from '../models';
import AbstractRepository from './AbstractRepository';

class UserRepository extends AbstractRepository {
  constructor () {
    super('User', [db.Clickwrap]);
  }
}

export default UserRepository;

Now I'm getting this error:

Cannot read property 'findAll' of undefined
LoneWolfPR
  • 3,978
  • 12
  • 48
  • 84
  • 2
    Why have you made `findAll` static? That is the reason. – loganfsmyth Jan 17 '17 at 22:50
  • [Never `export` a class instance!](http://stackoverflow.com/a/39079929/1048572) Export the class itself instead. – Bergi Jan 17 '17 at 23:05
  • @Bergi Never say never. Default export is great if a singleton is needed (not in this case). This doesn't mean that a class shouldn't be exported as named export too, at least for testing. – Estus Flask Jan 18 '17 at 06:16
  • @estus If you want to export a singleton, you should be using an object literal instead of `class` syntax. – Bergi Jan 18 '17 at 11:59
  • @Bergi This is a matter of taste to some degree, and I appreciate Occam's razor, but I don't think that it is applicable here. Generally speaking, classes are superior to object literals for singletons, just because of proper inheritance and [the performance of accessors](http://stackoverflow.com/questions/36338289/object-descriptor-getter-setter-performance-in-recent-chrome-v8-versions) (a bummer but that's true). Any way, that's offtopic, since the class in the question doesn't look like it needs to be a singleton. – Estus Flask Jan 18 '17 at 12:58
  • @Bergi (and everyone else) I changed my export, and now have a new error. I updated the post to reflect. Any more thoughts? – LoneWolfPR Jan 18 '17 at 14:35
  • Found my issue. The error is coming from the `findAll` method in the AbstractRepository. `this.model` is undefined because I'm calling everything statically, thus never calling the constructor. – LoneWolfPR Jan 18 '17 at 14:57

2 Answers2

2

Static functions are not callable on instances of class. See the last example on MDN documentation on static.

In your code, you're exporting an instance of UserRepository:

export default new UserRepository();

which you import later with the same name as class name:

import UserRepository from '../../repositories/UserRepository';

Now, when you do UserRepository.findAll(), you're invoking findAll function on an instance of UserRepository class. You imported that instance with the same name as the class name. You're not actually invoking the static function findAll on the class. Instead you're doing it on an instance.

As Bergi already suggested, just export the class:

export default UserRepository;
Ahmad Ferdous
  • 3,351
  • 16
  • 33
0

As I mentioned in a comment above, I was going about it the wrong way. Once I did as suggested and exported the class instead of an instance of the class I got an undefined error that I thought meant UserRepository wasn't defined in my user.js file. However, the error was actually in my findAll() method in my AbstractRepository because this.model was never set up because I was trying to do everything with static, thus never calling the constructor. So I gave up the static nonsense and instantiated an instance of my UserRepository. So the UserRepository looks like it does in the Edit part of my original post. However The AbstractRepository now looks like this:

import db from '../models';

export default class AbstractRepository {
  constructor (model, include) {
    if (new.target === AbstractRepository) {
      throw new TypeError('Cannot construct AbstractRepository instances directly.');
    }
    this.model = model;
    this.include = include;
  }
  findAll () {
    return db[this.model].findAll({ include: this.include });
  }
}

And my user.js file looks like this:

import UserRepository from '../../repositories/UserRepository';

const userRepo = new UserRepository();
const resolvers = {
  Query: {
    users (root, args, context) {
      return userRepo.findAll();
    },
  },
};

Now everything works great. Thanks for steering me in the right direction everyone.

LoneWolfPR
  • 3,978
  • 12
  • 48
  • 84