Data Modeling
MongoDB schemas with @nestjs/mongoose.
Overview
We use MongoDB for persistence with @nestjs/mongoose. The combination gives us schema flexibility alongside type-safe DTOs. Our tutorial resource (Task) demonstrates the recommended patterns below, but apply the same principles to production collections.
Schemas
- Use
@Schema()+@Prop()decorators to define models. - Enable timestamps to track created/updated dates (
{ timestamps: true }). - Prefer explicit field types (e.g.,
@Prop({ type: String, required: true, trim: true })). - Add defaults for booleans and enums to keep writes predictable.
@Schema({ timestamps: true })
export class Task {
@Prop({ type: String, required: true, trim: true })
title!: string;
@Prop({ type: String, required: false, trim: true })
description?: string;
@Prop({ type: Boolean, default: false, index: true })
done!: boolean;
}
export type TaskDocument = Task & Document;
export const TaskSchema = SchemaFactory.createForClass(Task);Indexing strategy
- Add indexes for frequent query filters (
done,createdAtranges). - Use compound indexes for combinations (
ownerId + status). - For text search, use MongoDB Atlas search or define text indexes with caution; they impact write performance.
- Periodically run
db.tasks.getIndexes()in non-prod to audit index drift.
TaskSchema.index({ done: 1, updatedAt: -1 });
TaskSchema.index({ title: 1 }, { unique: false, collation: { locale: "en", strength: 2 } });DTOs and validation
- Map DTOs with
class-validator+class-transformerto enforce input shape. - Keep DTOs separate from persistence models (e.g.,
CreateTaskDto,UpdateTaskDto). - Use
@IsString(),@IsOptional(),@IsBoolean()etc. to guarantee data quality. - Apply
ValidationPipeglobally with{ whitelist: true, transform: true }inmain.ts.
export class CreateTaskDto {
@IsString()
@IsNotEmpty()
title!: string;
@IsOptional()
@IsString()
description?: string;
}
export class UpdateTaskDto {
@IsOptional()
@IsString()
title?: string;
@IsOptional()
@IsBoolean()
done?: boolean;
}Relationships & references
- Prefer storing foreign keys as ObjectIds (
Types.ObjectId). - Populate references lazily to avoid large payloads; use
.populate()only where needed. - For denormalized views, create dedicated read models or use MongoDB aggregation pipelines.
Environment awareness
- Development uses local Docker or Atlas (see Local Development).
- Production uses managed clusters; keep connection strings in secrets (see Configuration & Environment).
- Always include read/write user roles scoped per environment.
Migrations
- MongoDB migrations can be handled via scripts or tools like migrate-mongo.
- Document schema changes in PR descriptions and update relevant tutorial content when field requirements change.