Database Collections
Database collections in Trailmix CMS provide a type-safe way to interact with MongoDB collections using your models.
Creating a Collection
Create a collection by extending AuditedCollection and implementing the required properties:
import { Collection } from 'mongodb';
import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
import { DocumentCollection, DatabaseService, Collections, AuditedCollection } from '@trailmix-cms/db';
import { TodoItem } from '../models';
import { CollectionName } from '../constants';
type Entity = TodoItem.Entity
const collectionName = CollectionName.TodoItem;
@Injectable()
export class TodoItemCollection extends AuditedCollection<Entity> implements OnModuleInit {
private readonly logger = new Logger(this.constructor.name);
public readonly collectionName = collectionName;
public readonly entitySchema = TodoItem.entitySchema;
constructor(
@DocumentCollection(collectionName)
protected readonly collection: Collection<Entity>,
protected readonly databaseService: DatabaseService,
protected readonly auditCollection: Collections.AuditCollection
) {
super(collection, databaseService, auditCollection);
}
async onModuleInit() {
this.logger.verbose(`creating custom indexes for collection_${collectionName}`)
await this.collection.createIndex({ list_id: 1 });
}
}
Using Collections
Inject collections into your services using the @DocumentCollection decorator:
import { Injectable } from '@nestjs/common';
import { DocumentCollection } from '@trailmix-cms/db';
import { TodoItemCollection } from './collections/todo-item.collection';
import { TodoItem } from './models/todo-item';
@Injectable()
export class TodoItemService {
constructor(
@DocumentCollection(CollectionName.TodoItem)
private readonly todoItemCollection: TodoItemCollection,
) {}
async findAll(): Promise<TodoItem.Entity[]> {
return this.todoItemCollection.find({});
}
async findById(id: string): Promise<TodoItem.Entity | null> {
return this.todoItemCollection.findById(id);
}
async create(todo: Omit<TodoItem.Entity, '_id' | 'created_at' | 'updated_at'>): Promise<TodoItem.Entity> {
return this.todoItemCollection.create(todo);
}
}
Audited Collections
All collections extend AuditedCollection which provides automatic audit trail functionality:
import { Collection } from 'mongodb';
import { Injectable } from '@nestjs/common';
import { DocumentCollection, DatabaseService, Collections, AuditedCollection } from '@trailmix-cms/db';
import { TodoList } from '../models';
import { CollectionName } from '../constants';
type Record = TodoList.Entity
const collectionName = CollectionName.TodoList;
@Injectable()
export class TodoListCollection extends AuditedCollection<Record> {
public readonly collectionName = collectionName;
public readonly entitySchema = TodoList.entitySchema;
constructor(
@DocumentCollection(collectionName)
protected readonly collection: Collection<Record>,
protected readonly databaseService: DatabaseService,
protected readonly auditCollection: Collections.AuditCollection
) {
super(collection, databaseService, auditCollection);
}
}
Audited collections automatically include:
created_by: User ID who created the documentupdated_by: User ID who last updated the document- Full audit trail of all changes stored in the audit collection
Non-Audited Collections
For collections that don't need audit trails, extend BaseCollection instead of AuditedCollection. This is useful for system collections or collections that don't require change tracking:
import { Collection } from 'mongodb';
import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
import { DocumentCollection, DatabaseService, BaseCollection } from '@trailmix-cms/db';
import { TodoItem } from '../models';
import { CollectionName } from '../constants';
type Entity = TodoItem.Entity
const collectionName = CollectionName.TodoItem;
@Injectable()
export class TodoItemCollection extends BaseCollection<Entity> implements OnModuleInit {
private readonly logger = new Logger(this.constructor.name);
public readonly collectionName = collectionName;
public readonly entitySchema = TodoItem.entitySchema;
constructor(
@DocumentCollection(collectionName)
protected readonly collection: Collection<Entity>,
protected readonly databaseService: DatabaseService
) {
super(collection, databaseService);
}
async onModuleInit() {
this.logger.verbose(`creating custom indexes for collection_${collectionName}`)
await this.collection.createIndex({ list_id: 1 });
await this.collection.createIndex({ completed: 1, created_at: -1 });
}
}
Key differences from audited collections:
- Extends
BaseCollection: Provides all standard CRUD operations without audit functionality - No audit context required: Methods like
insertOne,findOneAndUpdate,deleteOne, etc. don't require anauditContextparameter - No automatic audit trail: Changes are not automatically tracked in the audit collection
Type Safety
Collections are fully typed based on your model:
// TypeScript knows the return type
const todo: Todo | null = await todoCollection.findById(id);
// TypeScript validates the filter
const todos = await todoCollection.find({
completed: false, // ✅ Valid
invalidField: 'value' // ❌ TypeScript error
});
Custom Indexes
Use OnModuleInit to create custom indexes when the module initializes:
import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
@Injectable()
export class TodoItemCollection extends AuditedCollection<Record> implements OnModuleInit {
private readonly logger = new Logger(this.constructor.name);
// ... other properties ...
async onModuleInit() {
this.logger.verbose(`creating custom indexes for collection_${collectionName}`)
await this.collection.createIndex({ list_id: 1 });
// Add more indexes as needed
}
}
Next Steps
- Review the Configuration guide
- Check out the example applications for complete implementations