Problem with multiple mainModule-entrypoints for meteor package

Hello Community! This is my first post, so please be soft on me :wink:

We’ve recently started to transform our monolithic meteor application towards a modularised one, thus using meteor packages.
I want to manage all collections within a package (setup hooks, use denormalisation), say “core:collections”, and make our application-components use it. Moreover, I want to control which collections are exposed to 1) the client, 2) the server and 3) both. In a first version, I created three .js.-files (client, server, shared), defined the collections and used api.export in my package definition (in combination with api.addFiles) to expose the collections. This did work, however I no longer want to populate the global application scope that much, So I would like to use lazy loading instead. Thus, I defined api.mainModule for each of the js-files, and scoped them with either ‘client’, ‘server’ or [‘client’, ‘server’]. Unfortunately, these .mainModule-commands seem to overwrite each other, leading to certain collections not being exposed to the application. How can I fix that?

In short, is it possible to have something like this:

...
  api.mainModule('clientAPIs.js', 'client', { lazy: true })
  api.mainModule('serverAPIs.js', 'server', { lazy: true }) )
  api.mainModule('sharedAPIs.js', ['server', 'client'], { lazy: true }) )
...

? It does not seem to work, but maybe I’m missing something? Shall I drive another approach?

Thank you for your help!

As far as I know you can only have one mainModule for each environment. It should be relatively simple to architect your code using imports and exports to accomplish exposing the right code for each environment. When setting up my API’s I like to set setup 3 files (like you did initially) an then use import/export to control what code reaches which environment.

// common.js
export const WidgetCollection = new Mongo.Collection('widgets');

WidgetCollection.attachSchema(new SimpleSchema({
  // The schema for the collection
}));

export class WidgetModel extends SomeUsefulModelClass {
  commonMethod() {
    // a method that's common across environments
  }
}
// client.js
import { WidgetColleciton, WidgetModel } from './common.js';

WidgetModel.methods({
  clientMethod() {
    // A model method that should only be available on the client
  }
)};

// export modified API
export { WidgetsCollection, WidgetModel };
// server.js
import { WidgetCollection, WidgetModel } from './common.js';

WidgetModel.methods({
  serverMethod() {
    // A method that should only be exposed to the server
  }
});

WidgetCollection.allow({
  update(userId, doc) {
    return userId === doc.userId;
  }
});

// export modified API
export { WidgetCollection, WidgetModel };
// package.js
api.mainModule('clilent.js', 'client', { lazy: true })
api.mainModule('server.js', 'server', { lazy: true }) )

Thank you for your reply copleykj! Indeed, this seems to be a smart solution, though you would manage each api with a package, not all in one, but your approach is probably better anyway. Not sure though if I got this Model thing right…maybe I am just confused because WidgetModel.methods({...}) resembles a lot Meteor.methods({...}), so what is the idea here? This is most likely a newbie question, but could you maybe provide an example or reference some good sources? How to explicitly access server-methods or client-methods in the respective environments then? Thanks for your help in advance!

This is applicable whether you contain your API to a single package or spread it across many. If you keep it in a single package, you’ll just import all of the parts of your client side API into a single client file that then exports them all, and then all the parts of the server API into a single sever file and export them.

As far as the models, that’s not absolutely necessary. It’s just common to have models for your data and so I included it as an example.

The API for the models in the example is that of my socialize:base-model package, as it was designed to facilitate this type of API separation and all of the packages in the socialize set use it extensively.

1 Like