Search code examples
javascriptfeathersjs

How to use app.service('myService') inside a FeathersJS service extension?


I am trying to extend find() method for a service named properties in my FeathersJS app.

What I need is appending in all records returned by find() an array with integers coming from another service named propertyadds. This means I need to change my response from

{
  "total": 2973,
  "limit": 10,
  "skip": 0,
  "data": [
    {
      "id": 1,
      ...
    },
    {
      "id": 2,
      ...
    }
    ...
  ]
}

to

{
  "total": 2973,
  "limit": 10,
  "skip": 0,
  data: [
    {
      "id": 1,
      ...
      "additionals": [10,20,30]
    },
    {
      "id": 2,
      ...
      "additionals": [12,25,33]
    }
    ...
  ]
}

where the values in additionals come from service propertyadds, which manages a table like

| property_id | additional_id |
|-------------|---------------|
|      1      |       10      |
|      1      |       20      |
|      1      |       30      |
|      2      |       12      |
|      2      |       25      |
|      2      |       33      |
|-------------|---------------|

My initial idea was extending properties service like this

const { Service } = require('feathers-sequelize');

exports.Properties = class Properties extends Service {
  async find(data,params) {
      let newResponse = await super.find(data,params);
      let newData = newResponse.data.map(async pr => {
          pr.additionals = await app.service('propertyadds').find({
              properti_id: pr.id
          })
          return pr;
      })
      return {
          total: newResponse.total,
          limit: newResponse.limit,
          skip: newResponse.skip,
          data: newData
      }
  }
};

The problem is I don't have app inside src/services/properties/properties.class.js and (being new to FeathersJS as I am) I don't know how to get it.

What do I need to have a valid app const accessing all services inside this module?


Solution

  • The solution was, in fact, achieved when I visited src/services/properties.service.js and found out this line

    app.use('/properties', new Properties(options, app));
    

    So, service properties was, in fact, receiving an app object in its creation.

    Learning this made me see the correct solution, written in src/services/properties/properties.class.js like this:

    const { Service } = require('feathers-sequelize');
    
    exports.Properties = class Properties extends Service {
    
        constructor(options, app) {
            super(options, app);
            this.app = app;
        }
    
        async find(data, params) {
            let oldRes = await super.find(data, params);
            let newResData = oldRes.data.map(async pr => {
                let includedRecords = await this.app.service('propertyadds').find({
                    query: {
                        property_id: pr.id
                    }
                })
                pr.additionals = includedRecords.map(e => e.additional_id).sort();
                return pr;
            })
            return await Promise.all(newResData)
                .then(completed => {
                    return {
                        total: oldRes.total,
                        limit: oldRes.limit,
                        skip: oldRes.skip,
                        data: completed
                    }
                })
        }
    }
    

    As it may be seen, it was a matter of creating a constructor method for the extended Properties class, in order to expose the app object in this.app. This may be done after calling super(options,app) to make this available.

    After this it was just a matter of using this.app to create an instance of the other service and then making the correct async calls.