Feathers: Best practice for requiring services in modular file.

Created on 12 Feb 2018  路  8Comments  路  Source: feathersjs/feathers

Steps to reproduce

(First please check that this issue is not already solved as described
here
)

  • [ ] Tell us what broke. The more detailed the better.
  • [ ] If you can, please create a simple example that reproduces the issue and link to a gist, jsbin, repo, etc. This makes it much easier for us to debug and issues that have a reproducable example will get higher priority.

Expected behavior

I would like to use a service "outside" of a hook/service

Actual behavior

I cannot

System configuration

Latest and greatest everything

Whilst programming.. I usually put everything into a single huge file.
In my current predicament, I am writing everything in a custom class, in which I have access to the app object via;
````js
class Service {
setup(app) {
this.app = app;
}
...

var widgets= this.app.service('widgets');
````

However, I am trying to refactor my code and split it into separate functions.
I have tried everything possible (considering my JS abilities only slightly exceed my Feathers abilities).

What is the best way to do this? Pass the app/service to the function? Are/is there some files(s) I can require to get this working?

````js
// setYay.js
const widgets = require('../how/do/I/get it');

module.exports = (params) => {
widgets.create({text: 'yay!'});
};
````

Most helpful comment

Well in your case you have a circular dependency. app.js depends on writeWidget which tries to load app.js. Not sure why it doesn't just throw an error but I guess returning an empty module export is Nodes default behaviour in that case. What you really want to use in your case is service.setup to connect the app object. You can always pass it on to the function in writeWidget. In things.class.js:

/* eslint-disable no-unused-vars */
const writeWidgets = require('./writeWidgets');

class Service {
  constructor (options) {
    this.options = options || {};
  }

  async find (params) {
    await writeWidgets(this.app, 'yay!');
    return [];
  }

  setup(app) {
    this.app = app;
  }
}

module.exports = function (options) {
  return new Service(options);
};

module.exports.Service = Service;

In writeWidgets.js:

module.exports = async (app, text) => {
    await app.service('widgets').create({ text });
    console.log(text);
};

All 8 comments

You have to pass the app object. This can be done by requiring src/app or using a configure function:

In messages.service.js:

const memory = require('feathers-memory');

module.exports = function(app) {
  app.use('messages', memory({
    paginate: {
      default: 10,
      max: 25
    }
  }));
};

Then in another file:

const feathers = require('@feathersjs/feathers');
const memory = require('feathers-memory');
const configureMessages = require('./messages.service.js');

const app = feathers();

app.configure(configureMessages);

Thanks for your quick reply!
When you say "requiring src/app", I have tried with zero success things like;
js const app = require('../app'); and const widgets= require('../services/widgets/widgets.service');
and 20 other combinations..

Which was is best? it looks that that way will bypass the hooks and things already set up in the main Feather app.
Sorry if I'm not understanding properly.

I have create a test at https://github.com/ComputerCarl/test_feathers_service to demonstrate.

Yes, I meant you can do

const app = require('../app');

And then retrieve your services via app.service under the name the service is registered:

const widgets = app.service('widgets');

I thought this was a trivial matter that I just wasn't getting, but it's still not working.

I'd seen your solution and it looked so simple and familiar; it was one of the first things I'd tried after reading and re-reading two versions of the Feathers docs service sections. The following is the error;
````
C:UsersinterimProjectstest_feathers_servicesrcservicesthingswriteWidgets.js:6
const widgets = app.service('widgets');
^

TypeError: app.service is not a function
at Object. (C:UsersinterimProjectstest_feathers_servicesrcservicesthingswriteWidgets.js:6:21)
at Module._compile (module.js:643:30)
at Object.Module._extensions..js (module.js:654:10)
at Module.load (module.js:556:32)
at tryModuleLoad (module.js:499:12)
at Function.Module._load (module.js:491:3)
at Module.require (module.js:587:17)
at require (internal/module.js:11:18)
at Object. (C:UsersinterimProjectstest_feathers_servicesrcservicesthingsthings.class.js:2:22)
at Module._compile (module.js:643:30)
````

Well in your case you have a circular dependency. app.js depends on writeWidget which tries to load app.js. Not sure why it doesn't just throw an error but I guess returning an empty module export is Nodes default behaviour in that case. What you really want to use in your case is service.setup to connect the app object. You can always pass it on to the function in writeWidget. In things.class.js:

/* eslint-disable no-unused-vars */
const writeWidgets = require('./writeWidgets');

class Service {
  constructor (options) {
    this.options = options || {};
  }

  async find (params) {
    await writeWidgets(this.app, 'yay!');
    return [];
  }

  setup(app) {
    this.app = app;
  }
}

module.exports = function (options) {
  return new Service(options);
};

module.exports.Service = Service;

In writeWidgets.js:

module.exports = async (app, text) => {
    await app.service('widgets').create({ text });
    console.log(text);
};

Passing the app to the function makes perfect sense, especially after your explanation of what I was doing wrong; coding myself into a circle.

I was wondering why the imported app.js was always an empty object.

Thank you for your expertise once again and taking the time to explain this.

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue with a link to this issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings