You're Integrating Typescript and Mongoose Wrong!
Tags: typescript, programming, productivity, mongodb
Published: 2021-10-24 04:00:09

## 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](https://www.wikiwand.com/en/Don%27t_repeat_yourself) (Don't Repeat Yourself). To illustrate my point, please look at the following example given by the [official mongoose docs](https://mongoosejs.com/docs/typescript.html)

```ts
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](https://www.npmjs.com/package/@typegoose/typegoose). This library uses classes and [typescript decorators](https://www.typescriptlang.org/docs/handbook/decorators.html) 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](https://mongoosejs.com/docs/api/model.html) 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

![Talk is cheap. Show me the code.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o2y22hnhmmp6fj74psh7.jpg)

Since we're using decorators, we'll first have to add the following to our `tsconfig.json`:
```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. 😎

```ts
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](https://www.npmjs.com/package/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](https://typegoose.github.io/typegoose)!