DEV Community

How can I reduce amount of boilerplate code while working with TypeScript+mongoose 🙈

Konstantin Alikhanov on February 28, 2019

Let's say I have two entities: Vehicle and Tariff. They are related as 1-to-many (vehicle can has many tariffs). This is my basic types definitio...
Collapse
 
nickytonline profile image
Nick Taylor • Edited

You could do

type Entity<T> = T & {
    id: string;
    createdAt: Date;
}

// and use it like this:
const vehicle: Entity<VehicleOwnFields> = ...

Leveraging generics is probably the way to go.

Collapse
 
nuclight profile image
Konstantin Alikhanov

Ok, this is good one. Thank you.
How can I optimize other fields?

Collapse
 
nickytonline profile image
Nick Taylor

I'm on mobile at the moment. I'll post some other suggestions in a bit when I'm back at my laptop. On general though, union types and generics really help with these kinds of situations to make types more maintainable.

Thread Thread
 
nickytonline profile image
Nick Taylor • Edited

From there you could even do a Mongoose document generic type.

type Entity<T> = T & {
    id: string;
    createdAt: Date;
}

interface Vehicle {
    model: string;
    year?: string;
    seatsCount?: number;
}

type MongooseDocument<T> = T & mongoose.Document

const dbDocument: MongooseDocument<Entity<Vehicle>> = {
    SomeMongooseDocProperty: 'yolo',
    createdAt: new Date(),
    id: '5FCA1C32-1D92-47CA-A885-A5A747E6E4FB',
    model: 'Mini',
    seatsCount: 4,
    year: '1999'
}

You could refine this more if you wanted to make a Vehicle type which just wraps the Entity<T>.

You can see it in action in the enhanced TypeScript playground.

Also, this might interest you.

Thread Thread
 
nuclight profile image
Konstantin Alikhanov

Ok, I will play with it. Thank you.

Thread Thread
 
nickytonline profile image
Nick Taylor

You can even go a bit further.

interface BaseEntity {
  id: string;
  createdAt: Date;
}

type Entity<T> = T & BaseEntity;

interface Vehicle {
  model: string;
  year?: string;
  seatsCount?: number;
}

// Enforce having the bare minimum entity properties
type MongooseDocument<T extends BaseEntity> = T & mongoose.Document;

const dbDocument: MongooseDocument<Entity<Vehicle>> = {
  SomeMongooseDocProperty: "yolo",
  createdAt: new Date(),
  id: "5FCA1C32-1D92-47CA-A885-A5A747E6E4FB",
  model: "Mini",
  seatsCount: 4,
  year: "1999"
};

See the updated playground example

Collapse
 
michaeljota profile image
Michael De Abreu

You could always use something like TypeORM. I haven't try it myself, as the last time I needed an ODM I used camo but I think it has been deprecated now.

Collapse
 
ajsharp profile image
Alex Sharp 🛠sharesecret.co • Edited

I recently found typegoose, which seems promising and seems to attempt to solve this problem. Disclaimer: I haven't used it.

I will say, I'm about a year into a codebase with typescript and mongoose, and I wish I would've just started with TypeORM, and at some point I may switch. Mongoose seems like a project that's either already dead or on life-support.

Collapse
 
codemouse92 profile image
Jason C. McDonald

How do I replace boilerplate...

Simple: Use Python!

...

Yes, I am absolutely, undeniably kidding. I've heard that joke in my head every time I read your article title, since I posted!

(Honestly, though, I know nothing about TypeScript or mongoose, so I can't make any factual or useful comparisons to Python...and anyway, even if I could, it wouldn't be useful, since you already picked the stack you need!)

Collapse
 
nuclight profile image
Konstantin Alikhanov

I moved to Node.JS from Python :)

Collapse
 
gochev profile image
Nayden Gochev • Edited

Hey there you can use typegoose also you can completely remove the double Model/Schema generation and even the DTOs are not required..

Example with nestjs + typegoose + mongodb :

github.com/gochev/nest-js-poc-mongodb

Collapse
 
gochev profile image
Nayden Gochev

also I have added a blog post about that
dev.to/gochev/nestjs-mongo-typegoo...