Skip to content

Instantly share code, notes, and snippets.

@Grubba27
Last active May 2, 2025 15:19
Show Gist options
  • Save Grubba27/e128fe81fe2eb255919c826db57cdf18 to your computer and use it in GitHub Desktop.
Save Grubba27/e128fe81fe2eb255919c826db57cdf18 to your computer and use it in GitHub Desktop.
Meteor methods with decorators
// .babelrc needs this config at least:
// {
// "plugins": [
// ["@babel/plugin-transform-class-static-block"],
// ["@babel/plugin-proposal-decorators", { "version": "2023-11" }]
// ]
// }
import { Meteor } from 'meteor/meteor';
const makeMethodName = (namespace, methodName) => {
if (namespace) {
return `${namespace.toLowerCase()}.${methodName}`;
}
return methodName;
}
const safeAddMethod = (methodName, method) => {
if (Meteor.isServer && !Meteor.server.method_handlers[methodName]) {
Meteor.methods({[methodName]: method});
}
}
function expose(target, context) {
if (context.kind === "class") {
const className = context.name;
const methods = Object.getOwnPropertyNames(target.prototype).filter(
(name) => name !== "constructor" && !name.startsWith("_")
);
methods.forEach((methodName) => {
const method = target.prototype[methodName]
const functionName = makeMethodName(className, methodName);
safeAddMethod(functionName, method);
});
}
}
function rpc(target, context, ) {
if (context.kind === "method") {
const functionName = context.name;
context.metadata.isRpc = true;
return function (...args) {
const methodName = makeMethodName(this.constructor.name, functionName);
safeAddMethod(methodName, target)
return target.apply(this, args);
};
}
}
function params(...types) {
return (target, context) => {
if (context.kind === "method") {
context.metadata.params = types;
return function (...args) {
for (let i = 0; i < types.length; i++) {
const type = types[i];
const arg = args[i];
try {
const isOk = new type(arg);
if (isNaN(isOk)) {
throw new Meteor.Error(`Return value should be of type ${type}`)
}
} catch (e) {
throw new Meteor.Error(`Argument ${i} should be of type ${type}`);
}
}
return target.apply(this, args);
};
}
}
}
function returns(type) {
return (target, context) => {
if (context.kind === "method") {
context.metadata.returns = type;
return async function (...args) {
const r = await target.apply(this, args);
try {
const isOk = new type(r);
if (isNaN(isOk)) {
throw new Meteor.Error(`Return value should be of type ${type}`)
}
} catch (e) {
throw new Meteor.Error(`Return value should be of type ${type}`);
}
return r;
};
}
}
}
@expose
class Counter {
@params(Number)
@returns(Number)
async add(count) {
return count + 1
}
@params(String, String)
@returns(String)
concat(a,b) {
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment