NestJS Mongoose中DTO定义ObjectId及关联数据查询问题
Hey there! Let's break down your problems step by step—first fixing the type mismatch between MongoDB's ObjectId and your DTO's string type, then setting up the query to fetch related status data along with your tasks.
1. Fixing the ObjectId vs string Type Mismatch
The error pops up because MongoDB stores StatusId as an ObjectId (not a plain string), but your TasksDTO defines StatusId as a string. You have two solid options to resolve this:
Option 1: Update the DTO to Use GraphQL's ID Type
GraphQL's ID type natively handles both strings and ObjectIds (it automatically serializes ObjectIds to strings for the API response). Modify your TasksDTO like this:
import { ObjectType, Field, ID } from '@nestjs/graphql'; @ObjectType() export class TasksDTO { @Field(() => ID) id?: string; @Field() TaskDesc: string; @Field() StartedDate: string; @Field() EndDate: string; // Switch to ID type to support ObjectId serialization @Field(() => ID) StatusId: string; }
Option 2: Explicitly Convert ObjectId to string in the Service
If you prefer to keep the DTO's StatusId as a string, convert the ObjectId to a string in your service before returning data:
async getAllTasks(): Promise<TasksDTO[]> { const tasks = await this.todoItemsModel.find().exec(); // Map over tasks to convert ObjectId fields to strings return tasks.map(task => ({ id: task._id.toString(), TaskDesc: task.TaskDesc, StartedDate: task.StartedDate, EndDate: task.EndDate, StatusId: task.StatusId.toString() })); }
2. Fetching Relational Data (Include StatusDesc with Tasks)
To get the StatusDesc alongside your task data, use MongoDB's populate() method to join the Tasks and Statuses collections. Here's how to implement this:
Step 1: Update the Service Query with populate()
Modify your getAllTasks method to populate the StatusId field and fetch the related StatusDesc:
async getAllTasks(): Promise<any[]> { const tasks = await this.todoItemsModel.find() .populate({ path: 'StatusId', // Matches the ref field in your TaskSchema select: 'StatusDesc -_id' // Only fetch the StatusDesc field, exclude the Status _id }) .exec(); // Transform the data to match your desired output structure return tasks.map(task => ({ _id: task._id.toString(), TaskDesc: task.TaskDesc, StartedDate: task.StartedDate, EndDate: task.EndDate, StatusDesc: task.StatusId.StatusDesc // Access the populated status data })); }
Step 2: Create a Type-Safe DTO (Optional but Recommended)
For better type safety, create a new DTO that includes the StatusDesc field instead of using any:
import { ObjectType, Field, ID } from '@nestjs/graphql'; @ObjectType() export class TaskWithStatusDTO { @Field(() => ID) _id: string; @Field() TaskDesc: string; @Field() StartedDate: string; @Field() EndDate: string; @Field() StatusDesc: string; }
Then update your resolver to return this new DTO:
@Resolver('Tasks') export class ListTodoResolver { constructor(private readonly todoItemsService: TodolistService){ } @Query(() => [TaskWithStatusDTO]) async Tasks(): Promise<TaskWithStatusDTO[]> { return await this.todoItemsService.getAllTasks(); } // ... existing Statuses query }
This setup will return exactly the structure you're expecting:
[{ "_id": "5f9138f71163a739c43fc9b3", "TaskDesc": "Meeting with Brian", "StartedDate": "2020-10-22T07:42:40Z", "EndDate": "2020-10-22T10:42:40Z", "StatusDesc": "Done" }]
内容的提问来源于stack exchange,提问作者Harley Leung

