typegoose-patterns
MX Space project TypeGoose model patterns. Apply when creating data models, defining schemas, or handling MongoDB operations.
Installation
Follow the repository instructions for this skill and your agent. Review the files and dependencies before adding it to your workflow.
View source ↗GuidesSkill instructions
TypeGoose Model Patterns
Basic Model
import { modelOptions, prop, Severity, index } from '@typegoose/typegoose'
import { BaseModel } from '~/shared/model/base.model'
export const MY_COLLECTION_NAME = 'my_items'
@modelOptions({
options: {
customName: MY_COLLECTION_NAME,
allowMixed: Severity.ALLOW,
},
})
export class MyModel extends BaseModel {
@prop({ required: true })
name!: string
@prop()
description?: string
}
Base Model Inheritance
// BaseModel - Base fields
// Includes: _id, created, updated
// WriteBaseModel - Content models
// Includes: title, text, images, meta, modified
export class PostModel extends WriteBaseModel {
// Additional fields
}
// BaseCommentIndexModel - Supports comments
// Includes: commentsIndex, allowComment
Field Decorators
Basic Fields
@prop({ required: true }) // Required
name!: string
@prop() // Optional
description?: string
@prop({ default: true }) // Default value
isPublished: boolean
@prop({ unique: true }) // Unique constraint
slug!: string
@prop({ trim: true }) // Auto trim
title!: string
Indexes
// Single field index
@prop({ index: true })
slug!: string
// Class-level indexes
@index({ slug: 1 })
@index({ created: -1 })
@index({ title: 'text', text: 'text' }) // Text index
export class MyModel extends BaseModel {}
References
import { Ref } from '@typegoose/typegoose'
import { Category } from '~/modules/category/category.model'
// Reference field
@prop({ ref: () => Category, required: true })
categoryId: Ref<Category>
// Auto-populated virtual field
@prop({
ref: () => Category,
foreignField: '_id',
localField: 'categoryId',
justOne: true,
autopopulate: true,
})
public category: Ref<Category>
Nested Objects
// Define subdocument class
class Count {
@prop({ default: 0 })
read: number
@prop({ default: 0 })
like: number
}
// Usage
@prop({ type: Count, default: { read: 0, like: 0 }, _id: false })
count: Count
Arrays
@prop({ type: () => [String] })
tags?: string[]
@prop({ type: () => [ImageModel] })
images?: ImageModel[]
Plugins
import { plugin } from '@typegoose/typegoose'
import mongoosePaginate from 'mongoose-paginate-v2'
import aggregatePaginate from 'mongoose-aggregate-paginate-v2'
import autoPopulate from 'mongoose-autopopulate'
@plugin(mongoosePaginate) // Pagination
@plugin(aggregatePaginate) // Aggregate pagination
@plugin(autoPopulate) // Auto populate
export class MyModel extends BaseModel {}
Protected Keys
export class MyModel extends BaseModel {
@prop()
secret?: string
// Define fields that should not be directly updated
static get protectedKeys() {
return ['secret'].concat(super.protectedKeys)
}
}
Model Registration
In apps/core/src/processors/database/database.models.ts:
import { MyModel } from '~/modules/my/my.model'
export const databaseModels = [
// ...
MyModel,
]
Usage in Services
import { InjectModel } from '~/transformers/model.transformer'
import type { ReturnModelType } from '@typegoose/typegoose'
import type { AggregatePaginateModel } from '~/shared/types/mongoose.types'
@Injectable()
export class MyService {
constructor(
@InjectModel(MyModel)
private readonly model: ReturnModelType<typeof MyModel> &
AggregatePaginateModel<MyModel>,
) {}
// Query - use lean() to return plain object
async findById(id: string) {
return this.model.findById(id).lean()
}
// Pagination
async paginate(page: number, size: number) {
return this.model.paginate({}, { page, limit: size })
}
// Aggregate pagination
async aggregatePaginate(pipeline: any[], page: number, size: number) {
const aggregate = this.model.aggregate(pipeline)
return this.model.aggregatePaginate(aggregate, { page, limit: size })
}
}
Common Query Patterns
// Conditional query
await this.model.find({ isPublished: true }).lean()
// Sorting
await this.model.find().sort({ created: -1 }).lean()
// Field selection
await this.model.find().select('title slug').lean()
// Population
await this.model.find().populate('category').lean()
// Count
await this.model.countDocuments({ isPublished: true })
// Update
await this.model.findByIdAndUpdate(id, data, { new: true }).lean()
// Delete
await this.model.findByIdAndDelete(id)
Examples
Read the usage examples in the source repository.
View source ↗Source history
- Indexed on
- 2026-03-11
- Index updated
- 2026-03-11
- Branch
- main
Index and sync dates describe this directory. View the source repository for code history.
GitHub ↗chat Comments (0)
Sign in to join the discussion and leave a comment.
No comments yet. Be the first to share your thoughts!