Cheat sheet for Typescript and Mongoose with Mongo
I was recently working with mongodb, mongoose and typescript and I want to remember how the typing worked for mongoose models and Typescript.
Using typescript for Mongo with Mongoose
import mongoose from '../connections/mongoose.js'
import { DepartmentDto, DepartmentDocument } from '../some-other/type.ts'
import { OrganizationSettingsModel } from '../some-other-type/org.ts'
// This is a DTO interface, you can use this without any mongo/mongoose "stuff"
// Later we will pass this type into Mongoose's typescript helpers to shape the
// expected mongoose document instance
// model properties
export interface OrganizationDto {
name: string
logoId?: string
description?: string
// This is a sub document with a list of references to other mongo documents
departments: DepartmentDto[]
// This is a nested path, the nested doc is essentially a sub property
// but it does have its own schema - see https://mongoosejs.com/docs/subdocs.html#subdocuments-versus-nested-paths
settings?: IOrganizationSettings
}
// Documents are instances of mongoose models - i.e. when you Model.find() you will get back "documents". Here you override the DTO types with Mongoose document types. E.g. from this point onwards things will have "id" and "_id" applied. They will have virtuals applied if you define any.
// instance methods, virtual methods or properties and overrides
// note that you don;t have to redefine primitive typed properties here
export interface OrganizationDocument extends IOrganization, Document {
// This Types helper overrides the "settings" sub document property above to also have "_id" and "id" properties to
settings?: Types.Subdocument<Types.ObjectId> & IOrganizationSettings
// This Types helper overrides the "departments" property above to also have various
// mongoose document array methods like "push", "unshift", "addToSet" etc.
// note: we're dealing with documents here so we use the DepartmentDocument, not DTO
departments: Types.DocumentArray<DepartmentDocument>
}
// any static methods or properties
// The model is the actual mongoose model definition.
// This is where your "static" method definitions will be available
// I prefer to use a relevant "service" class instead of static methods but
// static methods could be used for something like OrganizationModel.from(someOtherObject)
export interface IOrganizationModel extends Model<IOrganizationDocument> {}
const schema = new mongoose.Schema<IOrganizationDocument, IOrganizationModel>(
{
name: { type: String, required: true },
logoId: { type: String, required: true },
departments: [
{ type: mongoose.Schema.Types.ObjectId, ref: 'DepartmentModel' },
],
settings: orgSettingsSchema,
},
{
toObject: {
virtuals: true,
},
methods: {
// instance methods can be defined here or using the schema.method() method
},
virtuals: {
// virtuals can be defined here or using the schema.virtual() method
},
statics: {
// statics can be defined here or using the schema.static() method
},
}
)
// virtuals will be on the document instance
schema.virtual('logoUrl').get(function () {
return + `${process.env.BASE_URL}/images/${this.logoId}` +
})
const OrganizationModel = mongoose.model<OrganizationDocument, IOrganizationModel>(
'Organization',
schema
)
export default Organization