The Problem
Typescript and mongoose is a headache I'm sure many of us has encountered. This is for one major reason - it severely goes against the principle of DRY (Don't Repeat Yourself). To illustrate my point, please look at the following example given by the official mongoose docs
import { Schema, model } from 'mongoose';
// 1. Create an interface representing a document in MongoDB.
interface User {
name: string;
email: string;
avatar?: string;
}
// 2. Create a Schema corresponding to the document interface.
const schema = new Schema<User>({
name: { type: String, required: true },
email: { type: String, required: true },
avatar: String
});
// 3. Create a Model.
const UserModel = model<User>('User', schema);
As you can see above, you're literally repeating the structure of the User
schema twice: once with typescript types and once with mongoose types. The added verbosity only grows as the complexity of your schema does, and soon enough it's a living nightmare 💀.
The Solution
Don't despair; there's still hope! Introducing typegoose. This library uses classes and typescript decorators to achieve what mongoose tries to do in twice as little code.
Keep in mind that typegoose doesn't actually modify any mongoose functionality. At the end of the day, you'll get a native mongoose Model to do whatever you want with.
How?
At it's most basic usage, typegoose revolves around:
- A class
- A class property decorator
- A class transforming function
Since we're using decorators, we'll first have to add the following to our tsconfig.json
:
{
"compilerOptions": {
// ...
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
Also, be sure to install mongoose
and @typegoose/typegoose
with the package manager of your choice.
Now, here's the same example given from the mongoose docs, but better. 😎
import { getModelForClass, prop } from '@typegoose/typegoose';
class User {
@prop()
name!: string;
@prop()
email!: string;
@prop()
avatar?: string;
}
const UserModel = getModelForClass(User);
Shorter, more readable, has nothing repeated, and returns the exact same thing!
Unless you override the type manually (through an option in the prop
decorator), typegoose will automatically infer the type of each property using reflect-metadata. Super cool, right? (Note that this gets a bit fuzzy around arrays).
There's so many more cool features and utilities with typegoose, such as creating static and instance methods directly inside of the class, that I would advise you to check out at the official docs!
Top comments (0)