I will be honest - we never used this kind of approach other than exclusively on the client.
At illustreets, we created a wrapper around SimplSchema that allows returning static schemas, or even building them on the fly and passing any arguments that may need to be dynamic, at run time (and yeah, many times from some async call - we had hundreds of those even before the Fibers crisis). Example:
import { IlluSchemas } from 'meteor/illustreets:schemas';
// a static schema
IlluSchemas.addSchema('CreateVisArgs', {
sourceSlug: {
label: 'Data source',
type: String,
},
filter: {
type: String,
optional: true,
autoform: {
type: 'hidden',
label: false,
},
},
});
// then somewhere else...
IlluSchemas.validate('CreateVisArgs', someObject);
// creates schema on-the-fly
export function getColumnsSchema(columns, prop, aliases) {
const fields = {};
columns.forEach((col) => {
fields[col.name] = {
type: Boolean,
label: aliases[col.name],
optional: true,
autoform: {
id: `${col.name}_${prop}`,
defaultValue() {
return col[prop];
},
},
};
});
return IlluSchemas.getSchema(fields);
}
// then elsewhere, on client...
const columns = Collection.findOne({ ... })?.columns;
// ... prop, aliases
getColumnsSchema(columns, prop, aliases).validate(someObject);
// on server...
const columns = (await Collection.findOne({ ... }))?.columns;
// ... prop, aliases
getColumnsSchema(columns, prop, aliases).validate(someObject);
This approach gives us the flexibility to still cache and reuse fields and entire schemas, and a few other features.
I don’t mind mixing up minimongo with SimplSchema on the client, given that minimongo is also a client construct, an in-memory structure instantiated just for the current user, at the end of the day. Also, I allow the client to be messy, cause most frontend stuff ends up messy somehow. But the server is a different beast. MongoDB is shared among multiple users, processes, and workers.
Also, the client part of your code really does not even need the Meteor.isClient check. The autoform block only runs on client, unless you are really trying something unholy. At illustreets we have multiple schemas with autoform blocks that are being populated just like that, used in common server/client code. But if we needed a shared allowedValues field with values from Mongo, we would generate the schema on the fly via IlluSchemas with allowedValues containing just an array of values passed as argument.
Oh, and here is a very recent comment from the creator of SimplSchema about the perils of trying to make allowedValues (and I assume other functions) async: AutoValue using async functions · Issue #484 · longshotlabs/simpl-schema · GitHub. Nearly impossible. It will simply mess up client side validations.
[EDIT]
I should add, obviously dynamic schemas like I describe above won’t work with Collection2, but for the cases where we need something like in your example, we run a validation in the services layer, just before inserting/updating the document. Collection2 would use a static version of the schema, to ensure the document has the expected structure.