mx-space / typegoose-patterns
Install for your project team
Run this command in your project directory to install the skill for your entire team:
mkdir -p .claude/skills/typegoose-patterns && curl -L -o skill.zip "https://fastmcp.me/Skills/Download/3907" && unzip -o skill.zip -d .claude/skills/typegoose-patterns && rm skill.zip
Project Skills
This skill will be saved in .claude/skills/typegoose-patterns/ and checked into git. All team members will have access to it automatically.
Important: Please verify the skill by reviewing its instructions before using it.
MX Space project TypeGoose model patterns. Apply when creating data models, defining schemas, or handling MongoDB operations.
0 views
0 installs
Skill Content
---
name: typegoose-patterns
description: MX Space project TypeGoose model patterns. Apply when creating data models, defining schemas, or handling MongoDB operations.
user-invocable: false
---
# TypeGoose Model Patterns
## Basic Model
```typescript
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
```typescript
// 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
```typescript
@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
```typescript
// 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
```typescript
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
```typescript
// 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
```typescript
@prop({ type: () => [String] })
tags?: string[]
@prop({ type: () => [ImageModel] })
images?: ImageModel[]
```
## Plugins
```typescript
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
```typescript
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`:
```typescript
import { MyModel } from '~/modules/my/my.model'
export const databaseModels = [
// ...
MyModel,
]
```
## Usage in Services
```typescript
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
```typescript
// 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)
```