feathersjs / feathers

The API and real-time application framework
https://feathersjs.com
MIT License
14.99k stars 743 forks source link

Dynamic services Question? #545

Closed kethan closed 7 years ago

kethan commented 7 years ago

Get Request http://localhost:3000/model/Message I Don't Know how to pass the req.params.ModelName to modelService as of now I am hard coding it modelService('Message')

const feathers = require('feathers');
const errorHandler = require('feathers-errors/handler');
const rest = require('feathers-rest');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const service = require('feathers-mongoose');
const Schema = mongoose.Schema;
const modelSchemas = [];
const MessageSchema = new Schema({
    text: { type: String, required: true }
});
modelSchemas.push({ modelName: 'Message', schema: MessageSchema });
mongoose.Promise = global.Promise;
mongoose.connect(MONGO_URL);

const app = feathers()
    .configure(rest())
    .use(bodyParser.json())
    .use(bodyParser.urlencoded({ extended: true }));
modelService = (modelName) => {
    // return (req, res, next) => {
    //     next()
    // }
    //TODO
    // const modelSchema = //Dynamically receive the schemas from elsewhere
    //TODO
    modelSchema = modelSchemas.filter(schema => schema.modelName == modelName);
    const Model = mongoose.model(modelSchema[0].modelName, modelSchema[0].schema);
    console.log(modelSchema[0].modelName);
    return service({
        Model: Model,
        lean: true,
        paginate: {
            default: 10,
            max: 100
        }
    });
   /* return {
        find(params) {
            return servicem.find(params);
        },
        get(id, params) {
            return servicem.get(id, params);
        },
        create(data, params) {
            return servicem.create(data, params);
        },
        update(id, data, params) {
            return servicem.update(id, data, params);
        },
        patch(id, data, params) {
            return servicem.patch(id, data, params);
        },
        remove(id, params) {
            return servicem.remove(id, params);
        }*/
    }
}

app.use('/model/:modelName', modelService('Message'));
const port = 3000;
app.listen(port, () => {
    console.log(`Feathers server listening on port ${port}`);
});

Thanks, Kethan

daffl commented 7 years ago

Ideally you would have all the names of your models during initialization time and register them then so that a client can't create arbitrary new services on the server.

It is however possible to create a wrapper service that probably does what you are looking for:

class DynamicService {
  find(params) {
    return this.getService(params.modelName).find(params);
  }

  get(id, params) {
    return this.getService(params.modelName).get(id, params);
  }

  create(data, params) {
    return this.getService(params.modelName).get(data, params);
  }

  update(id, data, params) {
    return this.getService(params.modelName).update(id, data, params);
  }

  patch(id, data, params) {
    return this.getService(params.modelName).patch(id, data, params);
  }

  remove(id, params) {
    return this.getService(params.modelName).remove(id, data, params);
  }

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

  getService(name) {
    if(!this.app.service(name)) {
      const modelSchema = modelSchemas.find(schema =>
        schema.modelName == name
      );
      const Model = mongoose.model(modelSchema.modelName, modelSchema.schema);

      this.app.use(`/${name}`, service({
          Model,
          lean: true,
          paginate: {
            default: 10,
            max: 100
          }
      }));
    }

    return this.app.service(name);
  }
}

app.use('/model/:modelName', new DynamicService());
kethan commented 7 years ago

Thank you @daffl but if I use this.app.use(/${name}, service({...}) that gets registered as endpoint right? What if I have tens and thousands of endpoints like that it will consume so much memory right? Actually, I am trying to build MBaaS using feathers which will have many applications with its own endpoint.

daffl commented 7 years ago

Services are a very small abstraction over the Express routes and event listeners that you will have to set up anyway so you will eventualy run into the same problems either way. Another option would be to fork the existing service adapter (https://github.com/feathersjs/feathers-mongoose) and change it so that it can handle the models dynamically based on params.modelName.

kethan commented 7 years ago

Hi @daffl

Yes, Thank you i will see the approach you have mentioned but that restrict itself only to mongoDB database :(

class DynamicService {
    find(params) {
        if (this.getService(params.modelName)) {
            return this.getService(params.modelName).find(params);
        } else {
            throw new errors.BadRequest('Not Found', { message: 'Model not found' });
        }
    }

    get(id, params) {
        return this.getService(params.modelName).get(id, params);
    }

    create(data, params) {
        return this.getService(params.modelName).get(data, params);
    }

    update(id, data, params) {
        return this.getService(params.modelName).update(id, data, params);
    }

    patch(id, data, params) {
        return this.getService(params.modelName).patch(id, data, params);
    }

    remove(id, params) {
        return this.getService(params.modelName).remove(id, data, params);
    }

    setup(app) {
        this.app = app;
    }
    getService(name) {
        const modelSchema = modelSchemas.find(schema => schema.modelName == name);
        modelService = null;
        if (modelSchema) {
            const Model = mongoose.model(modelSchema.modelName, modelSchema.schema);
            modelService = service({
                Model: Model,
                lean: true,
                paginate: {
                    default: 10,
                    max: 100
                }
            });
        }
        return modelService;
    }
}
app.use('/model/:modelName', new DynamicService());

Will the above approach is correct will this will also have problems in memory usage?

daffl commented 7 years ago

Your question was how to register a Mongoose service dynamically so yes, it will be only restricted to MongoDB.

Are you seeing any actual problems in memory usage? I think I gave you a starting point to work from, I can't tell you how to build a whole MBaaS in one GitHub issue 😉

kethan commented 7 years ago

No absolutely not :) just curious to know if there might me any serious memory usage issues I if I have tens and thousands of dynamic services ?

Thanks for the starting point.

daffl commented 7 years ago

I'm not sure to be honest. Most apps I work on have a maximum of maybe 10 services. I don't think several hundred will cause issues on a decently sized server, anything more than that might need to be scaled differently. Going to close this issue but let us know if there is anything else we can help you with!

lock[bot] commented 5 years ago

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.