I am trying to solve this:
I have a module, let’s say "users", for which I have a service with basic database operations (find, findMany, save etc).
I want to create more modules which all have the same basic database operations but I don’t want to have to write them all again
To make things easy, I have these 2 functions in an utils file:
export async function find<T extends Model>(model: T, query, options?): Promise<T> {
return model.find(query)
}
and
export async function findOne<T extends Model>(model: T, query, options?): Promise<T> {
return model.findOne(query)
}
How can I make it so I import each of this functions to a class (using TypeScript decorators or other TS magic)
I want to have something like this
import { attachAllBasicDatabaseMethods } from "./model-utils/operations"
export class UserController {
@attachAllBasicDatabaseMethods()
private constructor() {}
}
so in my controller i can use a method like this:
async getSomeUsers() {
const users = await this.findMany(UserModel, this.query, this.options);
return users;
}
The decorator idea is just something I have thought of, but I know there are some other methods of solving this
Or better: using that decorator with the model as the parameter automatically binds the model to the methods, so I can use this.find(this.query, this.options)
>Solution :
You could indeed do this with decorators. You could even make the decorators accept the model so you don’t need to specify it on each call to find/findOne:
Here’s a rough example:
import { find, findAll } from "./somewhere";
function attachAllBasicDatabaseMethods<T extends Model>(model: T) {
return function(target: any) {
Object.defineProperty(target.prototype, "find", {
value(query: any, options?: any): T { // TODO: Appropriate types for params
return find(model, query, options);
},
writable: true,
configurable: true,
});
Object.defineProperty(target.prototype, "findAll", {
value(query: any, options?: any): T { // TODO: Appropriate types for params
return findAll(model, query, options);
},
writable: true,
configurable: true,
});
};
}
Usage:
@attachAllBasicDatabaseMethods(UserModel)
class UserController {
// ...
}
That’s an example of a decorator factory. I made it a factory so that it could accept the model as a parameter and "bake it" into the find/findAll methods.